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.


