Java Generic List <Список <? расширяет число >>
Как в java мы не можем сделать:
List<List<? extends Number>> aList = new ArrayList<List<Number>>();
Даже если это нормально:
List<? extends Number> aList = new ArrayList<Number>();
Сообщение об ошибке компилятора:
Type mismatch: cannot convert from ArrayList<List<Number>> to List<List<? extends Number>>
Ответы
Ответ 1
В Java, если Car
является производным классом Vehicle
, мы можем рассматривать все Cars
как Vehicles
; a Car
является Vehicle
. Однако List
of Cars
также не является List
of Vehicles
. Мы говорим, что List<Car>
не ковариантно с List<Vehicle>
.
Java требует, чтобы вы явно указали это, когда хотите использовать ковариацию и контравариантность с подстановочными знаками, представленными токеном ?
. Посмотрите, где ваша проблема:
List<List<? extends Number>> l = new ArrayList<List<Number>>();
// ---------------- ------
//
// "? extends Number" matched by "Number". Success!
Внутренний List<? extends Number>
работает, потому что Number
действительно расширяет Number
, поэтому он соответствует "? extends Number
". Все идет нормально. Что дальше?
List<List<? extends Number>> l = new ArrayList<List<Number>>();
// ---------------------- ------------
//
// "List<? extends Number>" not matched by "List<Number>". These are
// different types and covariance is not specified with a wildcard.
// Failure.
Однако комбинированный параметр внутреннего типа List<? extends Number>
не соответствует List<Number>
; типы должны быть точно идентичными. Другой подстановочный знак подскажет Java, что этот комбинированный тип также должен быть ковариантным:
List<? extends List<? extends Number>> l = new ArrayList<List<Number>>();
Ответ 2
Я не очень хорошо знаком с синтаксисом Java, но, похоже, ваша проблема такова:
Ковариация и контравариантность
Ответ 3
Вы обязательно должны использовать? если это необходимо, не избегайте его в качестве общего правила. Например:
public void doThingWithList(List<List<? extends Number>> list);
позволяет передать List<Integer>
или List<Long>
.
public void doThingWithList(List<List<Number>> list);
позволяет передавать только аргументы, объявленные как List<Number>
. Небольшое различие да, но использование подстановочного знака является мощным и безопасным. Вопреки тому, как это может показаться, List<Integer>
не является подклассом или не присваивается, из List<Number>
. Не является List<Integer>
подклассом List<? extends Number
, поэтому приведенный выше код не компилируется.
Ответ 4
Ваш оператор не компилируется, потому что List<? extends Number>
не является тем же типом, что и List<Number>
. Первый - это супертип последнего.
Вы пробовали это? Здесь я выражаю, что List является ковариантным в своем аргументе типа, поэтому он примет любой подтип List<? extends Number>
(который включает в себя List<Number>
).
List<? extends List<? extends Number>> aList = new ArrayList<List<Number>>();
Или даже это. Здесь параметр типа для ArrayList в правой части такой же, как параметр типа в левой части, поэтому дисперсия не является проблемой.
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
Вы должны просто сказать
List<List<Number>> aList = new ArrayList<List<Number>>();
Я стараюсь избегать подстановок типа ?
, когда это возможно. Я считаю, что расходы, связанные с аннотацией типа, не приносят пользы.
Ответ 5
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
aList.add(new ArrayList<Integer>());