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

Кто-нибудь знает, почему следующий код не компилируется? Ни 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 

ошибка 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#128 of ? extends Foo>

ошибка 2:

IntelliJ дает мне

addAll(java.util.Collection<? extends capture<? extends Foo>>) in List cannot be applied to addAll(java.util.List<capture<? extends Foo>>)

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

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

5 ответов

Решение

(Я предполагаю, что здесь Bar а также Baz оба подтипа Foo.)

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

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

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

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

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

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

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

Это ошибки. Давайте изменим ваш код, учитывая, что Bar а также Baz два разных типа расширяющихся Foo:

List<? extends Foo> list1 = new ArrayList<Bar>();
List<? extends Foo> list2 = new ArrayList<Baz>();

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

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

Позвольте мне объяснить, в каком случае вам может понадобиться <? extend Classname>,

Итак, допустим, у вас есть 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 и несколько пап:

    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"));

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

        List<Grand> resultList;
        resultList = dads; // Error - Incompatable types List<Grand> List<Dad>
        resultList = grands;//Works fine

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

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

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

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

public class Bar extends Foo{ }

этот код:

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

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

Таким образом, удаление группового символа позволяет добавлять подклассы Foo.

Другие вопросы по тегам