Почему универсальный тип неприменим для аргумента extends super class для обоих?
Вот проблема, с которой я пытался найти решение.
У нас есть два определения класса. Один из двух расширяет другой.
class T{}
class TT extends T{}
Требование состоит в том, что должен существовать список, в котором хранится объект. T <
List<? extends T> list = new ArrayList<>();
Но проблема возникает, когда я пытаюсь поместить объект TT (едва ли он является подклассом T)
в список.
list.add(new TT());
Сообщение об ошибке компиляции
Метод add (capture # 2-of? extends Cell) в типе List не применим для аргументов (Cell)
Ответы
Ответ 1
Вы можете создать List<T> list = new ArrayList<T>();
напрямую, это позволит разрешить все подтипы T в список. Это на самом деле мало сложно понять. когда вы объявляете его как
List<? extends T> list = ...
Это означает, что он может разрешить любые неизвестные подтипы T
в списке. Но из этой декларации мы не можем гарантировать, что это точный подтип T
. поэтому мы можем добавить в него null
Ответ 2
Требование состоит в том, что должен существовать список, содержащий объект, который расширяет T
Если вы просто хотите List
, где вы можете хранить объекты любого класса, которые простираются от T
, просто создайте List
следующим образом:
List<T> list = new ArrayList<T>();
То, как вы создали список в настоящее время, не позволит вам добавлять что-либо, кроме null
к нему.
Ответ 3
List<? extends T>
указывает, что все, что может выйти из него, может быть передано в T, поэтому истинный список может быть любым из следующих:
-
List<T>
-
List<T2>
-
List<TT>
- и т.д.
Вы можете видеть, что даже новый T
нельзя добавить в такую коллекцию, потому что это может быть List<T2>
, в которое T
нельзя вставить. Таким образом, такой список не может содержать ненулевые записи, добавленные к ним.
В этом случае вы можете просто хотеть List<T>
Итак, зачем вы когда-либо использовали это?!
Эта контравариантность может быть полезна для параметров метода или возвратов, в которых коллекция будет считана, а не добавлена. Использовать для этого может быть создание метода, который принимает любую коллекцию, которая содержит элементы, которые являются T, или расширяет T.
public static void processList(Collection<? extends Vector3d> list){
for(Vector3d vector:list){
//do something
}
}
Этот метод может принимать любую коллекцию объектов, которая расширяет Vector3d, поэтому ArrayList<MyExtendedVector3d>
будет приемлемым.
В равной степени метод может возвращать такую коллекцию. Пример варианта использования описан в Возврат коллекции <ChildType> из метода, который указывает, что он возвращает Collection <ParentType> .
Ответ 4
Существуют граничные правила, определенные для Java Generics при использовании WildCards
**extends Wildcard Boundary**
Список означает список объектов, которые являются экземплярами класса T или подклассов T (например, TT). Это означает, что Чтение прекрасное, но вставка завершилась неудачно, поскольку вы не знаете, включен ли класс в T
**super Wildcard Boundary**
Когда вы знаете, что список набирается либо T, либо суперкласс T, можно вставить экземпляры T или подклассы T (например, TT) в список.
В вашем примере вы должны использовать "супер"