Oh Yeah
Oh Yeah

Reputation: 21

In the following example, is "List<? extends T>" necessary, or will "List<T>" do the same thing?

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

Answers (3)

Cratylus
Cratylus

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

Bohemian
Bohemian

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

Marko Topolnik
Marko Topolnik

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

Related Questions