I have a Storage
class:
class Storage<E> { void add(E e) { // add element }; void addAll(Iterable<? extends E> src) { for (E e : src) add(e); } }
There are two classes where class Child
extends
Parent
:
Parent
class Parent implements Comparable<Parent> { @Override public int compareTo(Parent o) { return 0; // some comparison logic } }
Child
class Child extends Parent { }
Driver class:
import java.util.Arrays; import java.util.List; public class GenericTest { public static void main(String[] args) { /******** CASE 1 *********/ Storage<Parent> ds = new Storage<Parent>(); ds.add(new Parent()); ds.addAll(Arrays.asList(new Parent())); // Type params are invariant. // But List<Child> is possible due to bounded wildcard type on addAll ds.addAll(Arrays.asList(new Child())); // Makes sense /******** CASE 2 *********/ List<Child> t = Arrays.asList(); max(t); } static <T extends Comparable<T>> T max(List<T> list) { return null; // Return null so code can compile } }
Because Storage
is a generic class, the operations on its method makes sense; I get how case 1 is working.
In case 2, with above signature of max
in GenericTest
, I get the compilation error:
The method max(List<T>) in the type GenericTest is not applicable for the arguments (List<Child>)
I understand that Comparable<Child>
is not a sub-type of Comparable<Parent>
(Typed params are invariant).
So, I updated signature to
// update Comparable<T> to Comparable<? super T> static <T extends Comparable<? super T>> T max(List<T> list)
Now, the code compiles and inferred signature is
<Child> Child GenericTest.max(List<Child> list)
This makes sense.
When I update the signature to
// update List<T> to List<? extends T> static <T extends Comparable<T>> T max(List<? extends T> list)
the code compiles and inferred signature is
<Parent> Parent GenericTest.max(List<? extends Parent> list)
I couldn’t understand how updating List<T>
to List<? extends T>
made the compiler infer the type Parent
.
I mean, for a generic method (on which there is direct invocation using type Child
), how did ? extends T
helped compiler refer the parent class of Child
? Note that this signature has Comparable<T>
(and not Comparable<? super T
).
Isn’t extends
about a type which is T
itself and its sub-classes?
Edit:
Java 8 comes with updated type-inference rules.
The below is sufficient for type-inference in Java 8 and later, but not for Java 7 and below:
static <T extends Comparable<T>> T max(List<? extends T> list)
It gives compilation error:
Bound mismatch: The generic method max(List<? extends T>) of type GenericTest is not applicable for the arguments (List<Child>). The inferred type Child is not a valid substitute for the bounded parameter <T extends Comparable<T>>
The signaure
static <T extends Comparable<? super T>> void max(List<T> list)
is sufficient for Java 7, though.
Advertisement
Answer
To make things clearer, the class Child
:
extends Parent
implements Comparable<Parent>
1) static <T extends Comparable<T>> T max(List<T> list)
T
isChild
- fails because
Child
is not aComparable<Child>
2) static <T extends Comparable<? super T>> T max(List<T> list)
T
isChild
?
isParent
- it works because
Child implements Comparable<Parent super Child>
3) static <T extends Comparable<T>> T max(List<? extends T> list)
T
isParent
?
isChild
- it works because
Child extends Parent implements Comparable<Parent>
Here in case 3), finding a valid T
class can be seen as: “find the first super class of Child
that implements a Comparable
of itself”.
As in case 1), it cannot be Child
because it is not a Comparable<Child>
.
The first (and only) super class of Child
that implements Comparable
of itself is Parent
.
I couldn’t understand how updating
List<T>
toList<? extends T>
made the compiler infer the typeParent
.
List<T>
forcesT
to beChild
List<? extends T>
forces?
to beChild
, notT