Reputation: 21
I understand that
List<? extends T>
allows for the list to be any sub-type of T (or T itself), and that
List<T>
only allows for lists of the type T. However, take a look at the following method signature:
public static <T extends Object & Comparable<? super T>> T findMax(List<? extends T> myList, int begin, int end){
And the following classes:
public class ClassA{
}
public class ClassB extends ClassA implements Comparable<ClassA>{
public int compareTo(ClassA s){
//do comparison
}
}
public class ClassC extends ClassB{
}
Let's assume T is ClassB, and I want to pass a sub-type of T (ClassC) for my list:
public static void main(String[] args){
List<ClassC> myC = new ArrayList<ClassC>();
ClassC a = findMax(myC, 2, 3);
}
In this case, how does java infer that T is ClassB, and not ClassC? And if it isn't able to infer ClassB (and actually infers ClassC instead) then wouldn't the following method signature (without the "List") be equivalent?
public static <T extends Object & Comparable<? super T>> T findMax(List<T> myList, int begin, int end){
Thanks, Jack
Upvotes: 1
Views: 463
Reputation: 54074
how does java infer that T is ClassB, and not ClassC?
I don't follow you here. Generics is a compile type technique.
The erasure of public static <T extends Object & Comparable<? super T>> T findMax(List<? extends T> myList, int begin, int end)
is to Object
i.e.
this compiles to public static Object findMax(List myList, int begin, int end)
When you instantiate the list List<ClassC> myC = new ArrayList<ClassC>();
and pass it to the method, the compiler ensures type safety, essentially that the pass list complies with the declaration. In this case Class C
implements a Comparable
and you could return back Class B
since the compiler would accept it.
Generics do not exist at runtime.
Update after comment:
This public static <T extends Object & Comparable<? super T>> T findMax(List<? extends T> myList, int begin, int end)
is not the same as:
public static <T extends Object & Comparable<? super T>> T findMax(List<T> myList, int begin, int end)
This is not about type inference. The compiler would make the same type inference here either it was T
or ? extends T
in this specific case.
The difference lies in the contract. The public static <T extends Object & Comparable<? super T>> T findMax(List<? extends T> myList, int begin, int end)
declares a method that guarantees not to modify your collection (at least not a way that would corrupt it).
The List<? extends T> myList
essentially makes list a read-only list inside the body of findMax
which can not add any instance in the list (either T
or subtype of T
) except null
.
If you declared List<T> myList
then it is not a read-only list and one could add elements in your list e.g. inside findMax
you could do: myList.add(myList.get(0));
This is not possible with the declaration of findMax
as List<? extends T> myList
Upvotes: 0
Reputation: 424983
Firstly, ? extends Object
adds no value, because everything extends Object, so these two methods are equivalent:
public static <T extends Object & Comparable<? super T>> T findMax(List<T> myList, int begin, int end)
public static <T extends Comparable<? super T>> T findMax(List<T> myList, int begin, int end)
Having made that simplification, your question is basically are these equivalent:
public static <T extends Comparable<? super T>> T findMax(List<T> myList, int begin, int end)
public static <T extends Comparable<? super T>> T findMax(List<? extends T> myList, int begin, int end)
They are (not* the same.
The reason is, with the second method, you can pass in a List with a type that's a subclass of the returned type, whereas in the first method the List's type must be the same type as the returned type.
Upvotes: 5
Reputation: 200148
The way type inference works, T
is always going to be inferred to the type param of the list you call the function with, so the additional liberty given by <? extends T>
will never be used. You can write List<T>
with the same result.
Answering to the subtler point raised by bohemian, the inferred type will be assignable to any supertype as well, so once again no flexibility is added by the longer signature.
Upvotes: 0