Списки с подстановочными знаками вызывают общую ошибку voodoo

Кто-нибудь знает, почему следующий код не компилируется? Ни add(), ни addAll() не работают. Удаление части "? Extends" заставляет все работать, но тогда я не смог бы добавить подклассы Foo.

List<!--? extends Foo--> list1 = new ArrayList<foo>();
 List<!--? extends Foo--> list2 = new ArrayList<foo>();
 /* Won't compile */
 list2.add( new Foo() ); //error 1
 list1.addAll(list2); //error 2 
</foo></foo>

ошибка 1:

IntelliJ говорит:

add(capture<!--? extends Foo-->) in List cannot be applied to add(Foo)

Компилятор говорит:

cannot find symbol
symbol : method addAll(java.util.List<capture#692 of="" ?="" extends="" foo="">)
location: interface java.util.List</capture#692>

ошибка 2:

IntelliJ дает мне

addAll(java.util.Collection<!--? extends capture<? extends Foo-->>) in List cannot be applied to addAll(java.util.List<capture<? extends="" foo="">>)
</capture<?>

В то время как компилятор просто говорит

cannot find symbol
symbol : method addAll(java.util.List<capture#692 of="" ?="" extends="" foo="">)
location: interface java.util.List<capture#128 of="" ?="" extends="" foo="">
 list1.addAll(list2);
</capture#128></capture#692>
5 ответов

(я предполагаю, что Bar и Baz оба являются подтипами Foo.)

List <!--? extends Foo--> означает список элементов некоторого типа, который является подтипом Foo, но мы не знаем, какой тип. Примерами таких списков могут быть ArrayList, a LinkedList и a ArrayList.

Поскольку мы не знаем, какой подтип является параметром типа, мы не можем помещать в него объекты Foo, а не объекты Bar или Baz. Но мы все же теперь, что параметр type является подтипом Foo, поэтому каждый элемент, который уже есть в списке (и который мы можем получить из списка), должен быть объектом Foo, поэтому мы можем использовать Foo f = list.get(0); и аналогичные вещи.

Такой список может быть использован только для элементов из списка, а не для добавления элементов вообще (кроме null, но я не знаю, разрешает ли это компилятор).

A List с другой стороны позволяет добавлять любой объект, который является объектом Foo, а в качестве Bar и Baz являются подтипами Foo, все объекты Bar и Baz являются Foo, поэтому они также могут быть добавлены.


Помните PECS: продюсер продюсеров, потребительский супер.

Поскольку вы пытаетесь добавить элементы в list2, это потребитель и не может быть объявлен как List <!--? extends Foo-->. Но тогда вы используете list2 как продюсер, когда добавляете его в list1. Поэтому list2 является как производителем, так и потребителем и должен быть List.

list1, как чистый потребитель, может быть List <!--? super Foo-->.


Там ошибки. Позволяет изменить ваш код, учитывая, что Bar и Baz являются двумя разными типами, расширяющими Foo:

List<!--? extends Foo--> list1 = new ArrayList<bar>();
List<!--? extends Foo--> list2 = new ArrayList<baz>();
</baz></bar>

Если разрешено list1.add(new Foo()), вы можете добавить экземпляры Foo в коллекцию, содержащую экземпляры Bar. Это объясняет первую ошибку.

Если разрешено list1.addAll(list2), все экземпляры Baz в списке2 будут добавлены в список1, который содержит только экземпляры Bar. Это объясняет вторую ошибку.


Позвольте мне попытаться объяснить, в каком случае вам может понадобиться использовать .

Итак, скажем, у вас есть 2 класса:

class Grand {
 private String name;
 public Grand(String name) {
 this.setName(name);
 }
 public Grand() {
 }
 public void setName(String name) {
 this.name = name;
 }
}
class Dad extends Grand {
 public Dad(String name) {
 this.setName(name);
 }
 public Dad() {
 }
}

И скажем, у вас есть 2 коллекции, каждая из которых содержит несколько Grands и некоторые Dads:

List<dad> dads = new ArrayList<>();
 dads.add(new Dad("Dad 1"));
 dads.add(new Dad("Dad 2"));
 dads.add(new Dad("Dad 3"));
 List<dad> grands = new ArrayList<>();
 dads.add(new Dad("Grandpa 1"));
 dads.add(new Dad("Grandpa 2"));
 dads.add(new Dad("Grandpa 3"));
</dad></dad>

Теперь давайте предположим, что мы хотим иметь коллекцию, которая будет содержать объекты Grand или Dad:

List<grand> resultList;
 resultList = dads; // Error - Incompatable types List<grand> List<dad>
 resultList = grands;//Works fine
</dad></grand></grand>

Как мы можем избежать этого? Просто используйте подстановочный знак:

List<!--? extends Grand--> resultList;
resultList = dads; // Works fine
resultList = grands;//Works fine

Обратите внимание, что вы не можете добавлять новые элементы в такую ​​(resultList) коллекцию. Для получения дополнительной информации вы можете прочитать о конкретизации подстановочных знаков и PECS в Java


Извините, может быть, я неправильно понял ваш вопрос, но допустил:

public class Bar extends Foo{ }

этот код:

List<foo> list2 = new ArrayList<foo>()
list2.add( new Bar() );
</foo></foo>

не генерируют никаких ошибок для меня.

Итак, удаление wild card позволяет добавлять подклассы Foo.

licensed under cc by-sa 3.0 with attribution.