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?
We use the Lower Bounded wildcards to widen the use of the type of variable. For example, if we want to add the list of integers in our method we can use the List<Integer>, but using this we will be bound to use only the list of integers.
A bounded wildcard is one with either an upper or a lower inheritance constraint. The bound of a wildcard can be either a class type, interface type, array type, or type variable. Upper bounds are expressed using the extends keyword and lower bounds using the super keyword.
The difference is on the compiler side. On the first one you can use the type (to cast something or use it as a bound to call another method for example) while on the second one, you cannot use it.
A lower bounded wildcard is expressed using the wildcard character ('? '), following by the super keyword, followed by its lower bound: <? super A>. Note: You can specify an upper bound for a wildcard, or you can specify a lower bound, but you cannot specify both.
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:
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
).
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());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With