Java Generics: назначение с вложенными параметрами подстановочных знаков

Для следующего примера кода:

public static class Abc<X> { }
public static class Def<Y> { }
public static class Ghi<Z> { }

public void doThis() {
    List<?> listOne;
    List<Abc<?>> listTwo;
    List<Abc<Def<?>>> listThree;
    List<Abc<Def<Ghi<?>>>> listFour;
    List<Abc<Def<Ghi<String>>>> listFive;

    Abc<Def<Ghi<String>>> abcdef;

    abcdef = new Abc<Def<Ghi<String>>>();

    listOne.add(abcdef);    // line 1
    listTwo.add(abcdef);    // line 2
    listThree.add(abcdef);  // line 3
    listFour.add(abcdef);   // line 4
    listFive.add(abcdef);   // line 5
}

Строки 1, 3 и 4 не компилируются:

(строка 1)

The method add(capture#1-of ?) in the type List<capture#1-of ?> is not applicable for the arguments (Abc<Def<Ghi<String>>>)

(строка 3)

The method add(Abc<Def<?>>) in the type List<Abc<Def<?>>> is not applicable for the arguments (Abc<Def<Ghi<String>>>)

(строка 4)

The method add(Abc<Def<Ghi<?>>>) in the type List<Abc<Def<Ghi<?>>>> is not applicable for the arguments (Abc<Def<Ghi<String>>>)

Строки 2 и 5, однако, компилируются.

Может ли кто-нибудь объяснить, почему строки 1, 3 и 4 не являются законными назначениями? И если параметры подстановочных знаков не могут использоваться таким образом в этих строках, то почему присвоение в строке 2 допустимо?

Ответ 1

listOne.add(abcdef) (строка 1) недействительна, поскольку List<?> представляет список неизвестного определенного типа. Например, это может быть List<String>, поэтому мы не хотим добавлять ничего, что не является String. Ошибка компилятора происходит потому, что Abc<Def<Ghi<String>>> не присваивается ?.

listTwo.add(abcdef) (строка 2) действительна, поскольку List<Abc<?>> представляет список Abc любого типа. Эти правые вложенные подстановочные знаки отличаются от подстановочных знаков верхнего уровня тем, что они представляют собой любой тип, а не определенный тип (другими словами, вложенные подстановочные знаки не capture). Компилятор допускает это, потому что Abc<Def<Ghi<String>>> можно присваивать Abc<?>. См. Это сообщение для дальнейшего обсуждения вложенных подстановочных знаков: Множественные подстановочные знаки для общих методов делают Java-компилятор (и меня!) Очень запутанным

listThree.add(abcdef) (строка 3) недействительна, поскольку List<Abc<Def<?>>> представляет список Abc of Def любого типа. Дженерики не ковариантны, поэтому Abc<Def<Ghi<String>>> не присваивается Abc<Def<?>>, хотя Def<Ghi<String>> присваивается Def<?>. A List<Integer> по той же причине не присваивается a List<Number>. См. Это сообщение для дальнейшего объяснения: Is List <Dog> подкласс списка <Animal> ? Почему неспецифические полиморфины Java неявно?

listFour.add(abcdef) (строка 4) недействительна по той же причине - Abc<Def<Ghi<String>>> не присваивается Abc<Def<Ghi<?>>>.

listFive.add(abcdef) (строка 5) действительна, потому что общие типы точно соответствуют - Abc<Def<Ghi<String>>>, очевидно, присваивается Abc<Def<Ghi<String>>>.