В чем разница между объявлением этого метода:
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>
(это не!!!).