Вот что означает текст, выделенный курсивом:
Параметр 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 (достаточно удобно, это пример главы, которую вы можете загрузить с веб-сайта книги) может рассказать больше об этой и других тонкостях дженериков.