ArrayList и Arrays.asList работают по-разному для Collection в случае наследования - PullRequest
0 голосов
/ 27 июня 2018
public class SS {
    public static void main(String[] args) {
        SS s = new SS();
        List<B> list = new ArrayList<>();

        s.checkWithoutInheritance(Arrays.asList(new B()));  //Works fine
        s.checkWithoutInheritance(list);    //----------Not working

        s.checkWithInheritance(Arrays.asList(new B())); //Works fine
        s.checkWithInheritance(list);   //Works fine
    }

    private void checkWithInheritance(final Collection<? extends A> s) {
    }

    private void checkWithoutInheritance(final Collection<A> s) {
    }
}


class A {
};

class B extends A {
};

Ответы [ 3 ]

0 голосов
/ 27 июня 2018
List<B> list;

Здесь объявляется переменная, представляющая собой список, с гарантией того, что все в этом списке может быть приведено B (то есть B, подкласс B или null), to which you can only add things which can be cast to B`.

private void checkWithoutInheritance(final Collection<A> s) {

Здесь объявляется метод, принимающий параметр, который является коллекцией, с гарантией того, что все в этой коллекции может быть приведено к A, к которому можно добавлять только вещи, которые можно привести к A.

Это означает, что checkWithoutInheritance может сделать это:

s.add(new A());

Если бы вы могли позвонить

checkWithoutInheritance(list);

тогда list теперь будет содержать экземпляр A, который нельзя преобразовать в B, что нарушает гарантию на содержимое list.

Как таковой, он запрещен компилятором. Компилятору не важно, что вы не добавите A к s, потому что вы могли бы . Ошибка на стороне безопасности.


С другой стороны:

private void checkWithInheritance(final Collection<? extends A> s) {

Здесь объявляется метод, принимающий параметр, который является коллекцией, с гарантией того, что все в этой коллекции является чем-то, что можно преобразовать в некоторый подкласс A.

Вы не можете добавить A к нему, потому что вы не знаете, означает ли «некоторый подкласс» A, B или что-то еще полностью.

Таким образом, вы не можете написать s.add(new A()); в его теле метода, поэтому безопасно передать ему List<B>.

0 голосов
/ 27 июня 2018

Когда у вас есть метод:

void checkWithoutInheritance(final Collection<A> s);

Линия:

s.checkWithoutInheritance(Arrays.asList(new B()));

отлично работает, потому что это так же, как:

List<A> list = Arrays.asList(new B());
s.checkWithoutInheritance(list);

Но, следующий код:

List<B> list = new ArrayList<B>();
s.checkWithoutInheritance(list); // compiler error

выдает ошибку компилятора, поскольку обобщенные значения не являются ковариантными , и List<B> нельзя присвоить List<A>:

List<B> listb = new ArrayList<B>();
List<A> lista = listb; // Incompatible types error, even though B extends A

Когда у вас есть метод:

void checkWithInheritance(final Collection<? extends A> s);

<? extends A> подстановочный знак ослабить ограничения на переменную s, поэтому оба действительны:

s.checkWithInheritance(new ArrayList<A>());
s.checkWithInheritance(new ArrayList<B>());

<? extends A> работает со списками A и подтипами A.

0 голосов
/ 27 июня 2018

Они не ведут себя по-разному, вы используете их по-разному.

Здесь вы позволяете компилятору определять тип List, возвращаемый Arrays.asList, и компилятор решает, что он должен быть List<A>, учитывая аргумент, требуемый checkWithoutInheritance():

s.checkWithoutInheritance(Arrays.asList(new B()));

Здесь вы передаете List<B> методу, поэтому у компилятора нет другого выбора, кроме как потерпеть неудачу:

List<B> list = new ArrayList<>();
s.checkWithoutInheritance(list);

Если бы вы использовали оба List одинаковые, вы бы получили одинаковый вывод:

Обе ошибки:

List<B> list = new ArrayList<>();
List<B> list2 = Arrays.asList(new B());
s.checkWithoutInheritance(list2); 
s.checkWithoutInheritance(list);

Оба работают:

s.checkWithoutInheritance(Arrays.asList(new B()));
s.checkWithoutInheritance(new ArrayList<>());

Что касается checkWithInheritance(final Collection<? extends A> s), он принимает и List<A>, и List<B>, поэтому оба ваших checkWithInheritance вызова проходят компиляцию.

...