Java generic method cannot call another generic method with looser constraint and return its value

Mark :

I would like to do this:

<T extends java.util.Date> T a(@Nonnull T... dates) {
    return b(dates);  // compile error
}

<T extends Comparable<T>> T b(T... comparables) {
   return comparables[0];
}

But it fails to compile unless I insert a cast within a:

<T extends java.util.Date> T a(@Nonnull T... dates) {
    return (T) b(dates);  // warning about unsafe cast in IntelliJ
}

<T extends Comparable<T>> T b(T... comparables) {
   return comparables[0];
}

Interestingly, if I remove the generic from a it works:

java.util.Date a(java.util.Date... dates) {
    return b(dates);
}

<T extends Comparable<T>> T b(T... comparables) {
   return comparables[0];
}

And if I port the original code to Kotlin, it also works (this makes me think it's a limitation of Java, and not something that is fundamentally unknowable):

fun <T: java.util.Date> a(dates: Array<T>): T {
    return b(dates);
}

fun <T: Comparable<T>> b(comparables: Array<T>): T {
    return comparables[0];
}

My question is: what is special about Java's type system that prevents this from compiling? It feels to me like the Java compiler could just insert the cast behind the scenes (it is my understanding that that is how generics are implemented in other cases).

Jacob G. :

A cast isn't required to get this to compile. Instead, you can use a bounded wildcard when specifying that T must extend Comparable<T>:

<T extends java.util.Date> T a(T... dates) {
    return b(dates);  // compiles fine
}

<T extends Comparable<? super T>> T b(T... comparables) {
    return comparables[0];
}

Note the Comparable<? super T> instead of Comparable<T>.

As Johannes Kuhn pointed out in his comment, a subclass of Date will implicitly implement Comparable<Date> instead of Comparable<DateSubclass>, hence the need for Comparable<? super T>.

For more information, see: What is PECS (Producer Extends Consumer Super)?

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=163510&siteId=1