Попытка понять роль супер подстановочного знака в Java Generics - PullRequest
2 голосов
/ 16 октября 2019

Я просматривал эту статью, чтобы понять роль подстановочного знака super в генериках. Я понял, как работает extends, но мне трудно понять, super

У меня есть ClassA, который расширен на ClassB, что делает ClassA суперклассомClassB.

Если я правильно понял статью, <? super ClassB> разрешит любой класс, который является супертипом ClassB.

У меня есть следующий код

GenericMethod.java

public class GenericMethod<T> {

    private List<T> list;

    public GenericMethod() {
        list = new ArrayList<>();
    }

    public void add(T t) {
        list.add(t);
    }

    public T get(int index) {
        return list.get(index);
    }
}

Driver.java

public class Driver {

    public static void main(String[] args) {

        GenericMethod<? super ClassB> genericMethod = new GenericMethod<>();

        ClassA classA = new ClassA();
        genericMethod.add(classA); // Compile-time error here

    }

}

Ошибка

The method add(capture#1-of ? super ClassB) in the type GenericMethod<capture#1-of ? super ClassB> is not applicable for the arguments (ClassA)

Я не понимаю, где я иду не так. Когда я создавал экземпляр класса GenericMethod, я уже объявил, что он примет любое значение, которое является супертипом из ClassB с объявлением <? super ClassB>. Таким образом, T внутри класса GenericMethod должен принимать все классы, которые расширяет ClassB.

Почему метод add выдает ошибку времени компиляции? Разве метод add не должен уже знать, что ему передан совершенно совместимый тип?

Ответы [ 2 ]

1 голос
/ 16 октября 2019

Предложение ? super является нижним знаком. Но граница относится к выводимому параметру типа, а не к ограничению типов аргументов, которые могут быть переданы методу, принимающему параметр этого универсального типа.

Когда вы говорите <? super ClassB>, выуказывают, что параметр типа может быть ClassB или любым супертипом, например ClassA или Object.

. Компилятор должен обрабатывать метод add так, как если бы он мог быть любой из этих сигнатур:

add(Object t)
add(ClassA t)
add(ClassB t)

(Могут быть другие типы, если ClassA унаследован непосредственно от другого класса вместо Object).

Компилятор должен отклонить ClassA в качестве аргумента addпотому что параметр типа может быть выведен как ClassB. Допустимо присваивать GenericMethod<ClassB> переменной genericMethod.

GenericMethod<? super ClassB> genericMethod = new GenericMethod<ClassB>();

Но не имеет смысла передавать ClassA методу, который ожидает ClassB.

На самом деле это то, что выведено оператором алмаза - ClassB.

Ваша путаница заключается в смешении двух понятий: какие параметры типов разрешены и какие типы объектов разрешеныв методах, которые используют параметр типа. Использование подстановочного знака ограничивает параметр типа, но метод по-прежнему принимает типы, являющиеся параметром типа или подтипом.

0 голосов
/ 16 октября 2019

Объявляя GenericMethod<? super ClassB>, вы заявляете, что тип является неизвестным типом, который является суперклассом ClassB (или самого ClassB). И просьба к компилятору разрешить добавление в список только подтипов этого неизвестного типа.

Единственные совместимые подтипы, которые знает компилятор, это ClassB и любые подтипы ClassB. При создании экземпляров обычно лучше избегать подстановочных знаков.

Для параметров метода подстановочные знаки дают вам больше гибкости в отношении того, что может быть принято. Используйте PECS (производитель = расширяет, потребитель = супер), чтобы определить, какой использовать. См. Слайд 5

...