What is the difference between this method declaration:
public static <E extends Number> List<E> process(List<E> nums){
and
public static List<Number> process(List<Number> nums){
Where would you use the former?
What is the difference between this method declaration:
public static <E extends Number> List<E> process(List<E> nums){
and
public static List<Number> process(List<Number> nums){
Where would you use the former?
The first allows process
of a List<Integer>
, a List<Double>
, etc. The second doesn't.
Generics in Java are invariant. They're not covariant like arrays.
That is, in Java, Double[]
is a subtype of Number[]
, but a List<Double>
is NOT a subtype of List<Number>
. A List<Double>
, however, is a List<? extends Number>
.
There are good reasons for generics being invariant, but that's also why the extends
and super
type are often necessary for subtyping flexibility.
super
and extends
for bounded wildcardsextends
Consumer super
" principleThe latter method (the one without <E extends Number>
) will only accept a parameter of exactly type List<Number>
and will it always
return a List<Number>
. For example, it will not accept List<Integer>
.
The former method (the one with <E extends Number>
) is a generic method, meaning it can accept different types of List
s
and it will return the same type of List
, as long as the List
s are lists of something that
extends Number
, e.g. List<Integer>
.
Example:
import java.util.ArrayList;
import java.util.List;
public class ProcessGenerics {
List<Number> listNumber = new ArrayList<Number>();
List<Integer> listInteger = new ArrayList<Integer>();
List<Double> listDouble = new ArrayList<Double>();
public static
List<Number> processWithoutExtends(List<Number> nums){ return nums; }
List<Number> resultN = processWithoutExtends(listNumber); // OK
//List<Integer> resultI = processWithoutExtends(listInteger); // compile-error - method not applicable
//List<Double> resultD = processWithoutExtends(listDouble); // compile-error - method not applicable
public static <E extends Number>
List<E> processWithExtends(List<E> nums){ return nums; }
List<Number> resultN2 = processWithExtends(listNumber); // OK
List<Integer> resultI2 = processWithExtends(listInteger); // OK
List<Double> resultD2 = processWithExtends(listDouble); // OK
}
See a similar explanation in the Wildcards chapter in the Generics Lesson in the Java Tutorials:
http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html
See also How to cast a list of inheriting objects to a collection of objects in Java? Both questions are really about generics and subtypes, e.g. whether List<Integer>
is a subtype of List<Number>
(it's not!!!).