Почему универсальный тип неприменим для аргумента 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) в список.

В вашем примере вы должны использовать "супер"