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