Почему передача подкласса в ограниченный шаблон допускается только в определенных местах? - PullRequest
3 голосов
/ 04 декабря 2009

Это из учебников по обобщению:

Скажем, класс R расширяет S,

public void addR(List<? extends S> s) {
    s.add(0, new R()); // Compile-time error!
}

Вы должны быть в состоянии выяснить, почему приведенный выше код запрещен. Тип второго параметра для s.add () является? extends S - неизвестный подтип S. Поскольку мы не знаем, что это за тип, мы не знаем, является ли он супертипом R; это может быть, а может и не быть таким супертипом, поэтому передавать туда R не безопасно. Я прочитал это несколько раз, но все еще не понимаю, почему следующее является ошибкой

С учетом подписи List.add ()

void add(int index, E element)

разве это не эквивалентно

void add(int index, <? extends S> element) // just to explain the idea, not a valid syntax

почему вызывается ошибка add (0, new R ()) R это S?

Ответы [ 2 ]

3 голосов
/ 04 декабря 2009

Вот что означает текст, выделенный курсивом:

Параметр s типа List<? extends S> может быть не просто экземпляром List<S> или List<R>, но и List<T> где T расширяется S.В этом случае, даже если R также расширяет S, R не обязательно расширяет T (они могут быть, например, братья и сестры в иерархии классов).Поскольку в такую ​​коллекцию можно поместить только значение типа T, во время компиляции компилятор не может гарантировать, что размещение R будет безопасным.

Чтобы привести более конкретный пример,Вы не можете добавить Double к List<? extends Number>, даже если Double расширяется Number!Это связано с тем, что переменной типа List<? extends Number> можно, например, присвоить List<Integer> во время выполнения, и добавление Double в такой список недопустимо.

Inна самом деле вы не можете вызвать метод add для списка, объявленного как List<? extends S>, потому что во время выполнения подстановочный знак всегда может представлять некоторый подтип S, который не является суперклассом того, что вы хотите добавить,Однако вы можете прочитать из такого списка, поскольку гарантируется, что подстановочный знак является подтипом S, и, следовательно, может быть назначен переменной типа S:

public S getElement(List<? extends S> s) {
  S result = s.get(0);
  return result;
}

Эта общая идея называется PECS (производитель-расширяет, потребитель-супер).Глава 5 Effective Java (достаточно удобно, это пример главы, которую вы можете загрузить с веб-сайта книги) может рассказать больше об этой и других тонкостях дженериков.

0 голосов
/ 04 декабря 2009

подумал, что это будет самое простое объяснение

Структура класса:

public class List<? extends S> {}
public class S {}
public class R extends S {}
public class T extends R {}

Использование кода:

List<T> list = new List<T>();

в этом случае недопустимо следующее:

list.add(new R());
...