Ничего неожиданного здесь не происходит. Давайте пройдемся по шагам:
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);
).