Общий список Java <Список <? расширяет номер >> - PullRequest
39 голосов
/ 14 апреля 2009

Почему в 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>>

Ответы [ 5 ]

73 голосов
/ 14 апреля 2009

В Java, если Car является производным классом Vehicle, тогда мы можем рассматривать все Cars как Vehicles; Car - это Vehicle. Однако List из Cars также не является List из 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 голосов
/ 14 апреля 2009

Вы обязательно должны использовать? при необходимости введите подстановочный знак, как правило, не избегайте его. Например:

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, поэтому приведенный выше код не компилируется.

3 голосов
/ 14 апреля 2009

Я не очень знаком с синтаксисом Java, но, похоже, ваша проблема заключается в следующем:

Ковариация и Контравариантность

2 голосов
/ 14 апреля 2009

Ваше утверждение не компилируется, потому что 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>>();

Я стараюсь по возможности избегать подстановочных знаков типа ?. Я считаю, что затраты, понесенные в виде аннотации типа, не стоят выгоды.

0 голосов
/ 18 мая 2012
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
aList.add(new ArrayList<Integer>());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...