Подтвердить что ты не робот

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>>

4b9b3361

Ответ 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>>();

Ответ 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>());