В чем разница между объявлением этого метода:
public static <E extends Number> List<E> process(List<E> nums){
и
public static List<Number> process(List<Number> nums){
Где бы вы использовали первый?
В чем разница между объявлением этого метода:
public static <E extends Number> List<E> process(List<E> nums){
и
public static List<Number> process(List<Number> nums){
Где бы вы использовали первый?
Первый позволяет process a List<Integer>, a List<Double> и т.д. Второй не делает.
Дженерики в Java инвариантны. Они не ковариантны, как массивы.
То есть, в Java, Double[] является подтипом Number[], но List<Double> НЕ является подтипом List<Number>. A List<Double>, однако, есть List<? extends Number>.
Имеются веские причины для того, чтобы дженерики были инвариантными, но также почему типы extends и super часто необходимы для гибкости подтипирования.
super и extends для ограниченных подстановочных знаковextends Потребитель super" Последний метод (один без <E extends Number>) будет принимать только параметр точно типа List<Number> и всегда будет
верните a List<Number>. Например, он будет не принимать List<Integer>.
Первый метод (один с <E extends Number>) является общим методом, что означает, что он может принять различные типы List s
и он вернет тот же тип List, если List - это списки того, что
extends Number, например. List<Integer>.
Пример:
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
}
См. аналогичное объяснение в главе "Подстановочные знаки" в уроке Generics в учебниках Java:
http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html
См. также Как передать список наследуемых объектов в коллекцию объектов в Java? Оба вопроса действительно касаются дженериков и подтипов, например. является ли List<Integer> подтипом List<Number> (это не!!!).