Принципы Get-Put в Java дженериках - PullRequest
0 голосов
/ 22 января 2020

В последнее время я изучал Java дженерики и натолкнулся на так называемый принцип "get-put", то есть, какие подстановочные знаки позволяют добавлять или удалять определенные типы объектов из коллекции (например, https://flylib.com/books/en/4.79.1.18/1/).

Моя проблема в том, что говорят, что вы не можете получить ничего, кроме Объекта (ов) из коллекции, которая использует <? super SomeClass>. Но следующий код совершенно корректен:

List<? super A> list = new ArrayList<>();
list.add(new A());
System.out.println((list.get(0).toString()));

, где

class A{
    @Override
    public String toString(){
        return "super.toString();";
    }
}

Самое смешное, что он действительно использует переопределенную toString (), вопреки принципу.

Кроме того,

 A a = list.get(0);

не удается.

Может кто-нибудь объяснить, в чем здесь проблема?

Ответы [ 2 ]

1 голос
/ 22 января 2020

Ничего неожиданного здесь не происходит. Давайте пройдемся по шагам:

List<? super A> list = new ArrayList<>();

У нас есть List из ?, которые выше A в иерархии классов. Каждый ? является Object, поэтому мы можем думать о нем как List<Object>. Продолжаем дальше ...

list.add(new A());
System.out.println((list.get(0)

Пока все хорошо - list имеет A внутри, и его выбирают как Object.

                               .toString()));

Мы называем toString, который вызывается на Object (A). Диспетчеризация Dynami c продолжает вызывать метод A toString (самое низкое определение из иерархии типов). Однако это совершенно законно, поскольку toString определено как для Object, так и для A. Двигаясь дальше ...

 A a = list.get(0); //oops!

Это ломается, как и ожидалось, потому что мы пытаемся преобразовать Object из List<Object> в A, без приведения (например, A a = (A) list.get(0);).

1 голос
/ 22 января 2020

Тип ссылки list.get() в этом случае Object, и этот класс объявляет toString(), поэтому вам разрешено его вызывать.

Но Java использует dynamici c отправить , чтобы выбрать, какой метод выполнить. И так как тип времени выполнения объекта - A, то называется эта версия.

Ваш код эквивалентен этому:

Object obj = new A();
System.out.println(obj.toString()); /* Prints A's version */
A a = obj; /* Fails to compile. */

Как видите, поведение имеет ничего общего с дженериками.

...