Java В чем разница между <? super T> и <? extends T>?

В чем разница между <? super T> и <? extends T>? Когда нужно использовать <? super T>, а когда <? extends T>?

Лично моё понимание разницы между ними состоит в следующем: extends
Если мы используем <? extends T>, например на List: List<? extends Number> foo - это означает, что любое из них является юридическим назначением:

List<? extends Number> foo = new ArrayList<Number>();  // Number "расширяет" Number
List<? extends Number> foo = new ArrayList<Double>();  // Double расширяет Number
List<? extends Number> foo = new ArrayList<Integer>(); // Integer расширяет Number

Если рассматривать чтение данных их коллекций выше, то можно сказать следующее:

  • мы сможем считать значение из коллекции foo и записать её в переменную типа Number, поскольку коллекция foo содержит элементы типа Number или элементы типа подкласса Number (то есть возможно неявное приведение к Number)
  • из коллекции foo мы не сможем считать значение Integer, потому что foo может указывать на List
  • из коллекции foo мы не сможем считать значение Double, потому что foo может указывать на List

Теперь же о записи в коллекцию List foo, который был бы законным для всех выше возможных ArrayList присвоения:

  • мы не сможем записать Integer, поскольку foo может указывать на List.
  • мы не сможем записать Number, поскольку foo может указывать на List.
  • мы не сможем записать Double, поскольку foo может указывать на List.

Мы не можем добавлять объект в коллекцию List<? extends T>, поскольку не можем гарантировать что List действительно будет указывать на тип нашего объекта (то есть, что гарантировать, что добавление элемента разрешено) Единственная "гарантия" заключается в том, что мы сможем считать и получить экземпляр класса T или экземпляр подкласса T.

Если мы используем <? super T>, например на List<? super Integer> foo - это означает, что любое из них является юридическим назначением:

List<? super Integer> foo = new ArrayList<Integer>();  // Integer is a "superclass" of Integer
List<? super Integer> foo = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo = new ArrayList<Object>();   // Object is a superclass of Integer

Если мы будем пытаться считать элемент учетом вышеперечисленных назначений, какой тип объекта мы сможем получить при чтении из List foo:

  • не гарантируется Integer, поскольку foo может указывать на List или List.
  • не гарантируется Number, поскольку foo может указывать на List.
  • гарантия заключается только в том, что мы получим экземпляр Object или подкласса Object (н не можем сказать какой именно).

Теперь же о записи в коллекцию List foo, который был бы законным для всех выше возможных ArrayList присвоения:

  • мы можете добавить элемент типа Integer, поскольку Integer разрешен в любом из перечисленных выше списков.
  • мы можете добавить экземпляр подкласса Integer, поскольку экземпляр подкласса Integer разрешен в любом из приведенных выше списков.
  • мы можете добавить элемент типа Integer, поскольку Integer разрешен в любом из перечисленных выше списков.
  • мы не можем добавить Double, поскольку foo может указывать на ArrayList.
  • мы не можем добавить Number, поскольку foo может указывать на ArrayList.
  • мы не можем добавить Object, поскольку foo может указывать на ArrayList.

PECS:

  • "Producer Extends" . Если вам нужен List для создания значений T (вы хотите прочитать T из списка), вам нужно объявить его ? extends T, например List<? extends Integer>. Но вы не можете добавить в этот список.

  • "Потребительский супер" . Если вам нужен List для использования значений T (вы хотите записать T в список), вам нужно объявить его ? super T, например List<? super Integer>. Но нет никаких гарантий того, какой тип объекта вы можете прочитать из этого списка.

  • Если вам нужно как читать, так и записывать в список, вам нужно объявить его точно без каких-либо подстановочных знаков, например. List.

1 Вподобання