В чем разница между <E extends Number> и <Number>?

В чем разница между объявлением этого метода:

public static <E extends Number> List<E> process(List<E> nums){

и

 public static List<Number> process(List<Number> nums){

Где бы вы использовали первый?

Ответы

Ответ 1

Первый позволяет process a List<Integer>, a List<Double> и т.д. Второй не делает.

Дженерики в Java инвариантны. Они не ковариантны, как массивы.

То есть, в Java, Double[] является подтипом Number[], но List<Double> НЕ является подтипом List<Number>. A List<Double>, однако, есть List<? extends Number>.

Имеются веские причины для того, чтобы дженерики были инвариантными, но также почему типы extends и super часто необходимы для гибкости подтипирования.

См. также

Ответ 2

Последний метод (один без <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> (это не!!!).