7

I was following a tutorial about generics in Java defining this static method:

public static <T extends Comparable<T>> T min(T a) { ... }

and saying that

min(new GregorianCalendar());

couldn't compile because GregorianCalendar extends Calendar and Calendar implements Comparable<Calendar> so it implied that GregorianCalendar implements Comparable<Calendar> and NOT Comparable<GregorianCalendar>. So in order to compile the signature must be changed into:

public static <T extends Comparable<? super T>> T min(T a) { ... }

which is totally understandable. The 1st version of the method effectively doesn't compile in java-5 but it compiles in java-8! (i tried 5 through 8)

Why java-8 now allows that? (because it makes it more confusing now). What's the new "rule" behind that?

Holger
  • 285,553
  • 42
  • 434
  • 765
sweetacaro
  • 105
  • 6

2 Answers2

4

Type inference!

There is a significant amount of information regarding this in JLS §18. Specifically, I'll direct you to JLS §18.2 (page 678) which states:

enter image description here

In your case, let S = GregorianCalendar and T = Calendar. This page states (during the reduction process) if S is a sub-type of T, then S is considered to be of type T (GregorianCalendar is treated as Calendar).

Jacob G.
  • 28,856
  • 5
  • 62
  • 116
  • 5
    Terminology in JLS §18 is a bit more complex, the contraint formula `‹S <= T›` indicates that _"A type argument S is contained by a type argument T"_ (see [§18.2.1](https://docs.oracle.com/javase/specs/jls/se9/html/jls-18.html#jls-18.1.2)), not a sub-type relation as the answer indicates. – Stephan Herrmann Feb 10 '18 at 12:59
1

The problem with your example is that you have written

min(new GregorianCalendar());

as a standalone expression. Prior to Java 8, this would be handled by the compiler by inferring GregorianCalendar for T which does not fulfill the constraint “… extends Comparable<T>”, so it gets rejected.

Starting with Java 8, the compiler infers Calendar for T, which fulfills the constraint, and it is valid to pass an instance of GregorianCalendar where Calendar is expected, so the example is accepted. It’s like if you hay written YourClass.<Calendar>min(new GregorianCalendar()); in previous Java versions.

However, this does not solve the fundamental problem of the min declaration that it doesn’t accept GregorianCalendar for T. While Java 8 allows you to write, e.g.

Calendar c = min(new GregorianCalendar());

using Calendar for T, it is still invalid to use GregorianCalendar for T, e.g.

GregorianCalendar c = min(new GregorianCalendar());

still will be rejected by the compiler.

Therefore, it is still necessary to use the declaration with wildcard

public static <T extends Comparable<? super T>> T min(T a) { ... }

to allow callers to use it like

GregorianCalendar c = min(new GregorianCalendar());
Holger
  • 285,553
  • 42
  • 434
  • 765