简体   繁体   中英

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 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM