Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java6, Guava, generics, type inference

I've written an utility method in Java:

public static final ImmutableSortedSet<TimeUnit> REVERSED_TIMEUNITS = ImmutableSortedSet.copyOf(
        Collections.<TimeUnit>reverseOrder(),
        EnumSet.allOf(TimeUnit.class)
);


/**
 * Get the number of ..., minutes, seconds and milliseconds
 *
 * You can specify a max unit so that you don't get days for exemple
 * and can get more than 24 hours if you want to display the result in hours
 *
 * The lowest unit is milliseconds
 * @param millies
 * @param maxTimeUnit
 * @return the result map with the higher unit first
 */
public static Map<TimeUnit,Long> getCascadingDateDiff(long millies,TimeUnit maxTimeUnit) {
    if ( maxTimeUnit == null ) {
        maxTimeUnit = TimeUnit.DAYS;
    }
    Map<TimeUnit,Long> map = new TreeMap<TimeUnit,Long>(Collections.<TimeUnit>reverseOrder());
    long restInMillies = millies;
    Iterable<TimeUnit> forUnits = REVERSED_TIMEUNITS.subSet(maxTimeUnit,TimeUnit.MICROSECONDS); // micros not included
    // compute the number of days, then number of hours, then minutes...
    for ( TimeUnit timeUnit : forUnits ) {
        long numberForUnit = timeUnit.convert(restInMillies,TimeUnit.MILLISECONDS);
        map.put(timeUnit,numberForUnit);
        restInMillies = restInMillies - timeUnit.toMillis(numberForUnit);
    }
    return map;
}

It works with:

    Map<TimeUnit,Long> map = new TreeMap<TimeUnit,Long>(Collections.reverseOrder());

But I first tried with

    Map<TimeUnit,Long> map = Maps.newTreeMap(Collections.reverseOrder());

My IntelliJ doesn't say anything, while my compiler says:

DateUtils.java:[302,48] incompatible types; no instance(s) of type variable(s) K,V exist so that java.util.TreeMap conforms to java.util.Map [ERROR] found : java.util.TreeMap [ERROR] required: java.util.Map

It works fine without the comparator:

   Map<TimeUnit,Long> map = Maps.newTreeMap();

But I tried with:

Map<TimeUnit,Long> map = Maps.newTreeMap(Collections.<TimeUnit>reverseOrder());

And with:

Map<TimeUnit,Long> map = Maps.newTreeMap(new Comparator<TimeUnit>() {
    @Override
    public int compare(TimeUnit timeUnit, TimeUnit timeUnit1) {
        return 0; 
    }
});

And i got the same error. So it seems each time i'm using a comparator in the TreeMap, type inference doesn't work anymore. Why?


The signature of the Guava method is:

  public static <C, K extends C, V> TreeMap<K, V> newTreeMap(Comparator<C> comparator)

The expected return type is of type so without a comparator, Java is able to infer that K = TimeUnit and V = Long.

With a comparator of type TimeUnit, Java knows that C is TimeUnit. It also knows that the expected return type is of type so K = TimeUnit and V = Long. K extends C is respected since TimeUnit extends TimeUnit (anyway I tried also with an Object comparator if you think it's wrong...)

So i just wonder why type inference doesn't work in this case?

like image 941
Sebastien Lorber Avatar asked Jun 08 '12 08:06

Sebastien Lorber


People also ask

What is infer generic type arguments?

Type inference represents the Java compiler's ability to look at a method invocation and its corresponding declaration to check and determine the type argument(s). The inference algorithm checks the types of the arguments and, if available, assigned type is returned.

What is Java type inference?

Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.

What are target types in Java?

The Target-Type of an expression is the data type that the Java Compiler expects depending on where the expression appears. Java 8 supports inference using Target-Type in a method context.


1 Answers

Like Michael Laffargue suggested, it is an OpenJDK6 type inference bug:

https://bugs.openjdk.java.net/show_bug.cgi?id=100167

http://code.google.com/p/guava-libraries/issues/detail?id=635

It works fine in my IntelliJ, and with a OpenJDK in version 7, and with others JDK in version 6.


The following suggestion of kutschkem works:

    Map<TimeUnit,Long> map = Maps.<TimeUnit,TimeUnit,Long>newTreeMap(Collections.<TimeUnit>reverseOrder());

Notice the <TimeUnit,TimeUnit,Long> which permits to force the typed parameters explicitly. Check this related topic: What's this generics usage in Java? X.<Y>method()

Thanks all

like image 60
Sebastien Lorber Avatar answered Sep 23 '22 05:09

Sebastien Lorber