10

Разница между <? super T> и <? extends T> в Java

13

Вопрос о различиях между List<? super T> и List<? extends T> в Java

Я столкнулся с проблемой при работе с обобщениями в Java и не могу понять разницу между двумя типами списков: List<? super T> и List<? extends T>.

Я использовал List<? extends T>, но заметил, что не могу добавлять элементы в этот список с помощью метода list.add(e). В то же время, с использованием List<? super T> у меня не возникает этой проблемы, и я могу успешно добавлять элементы.

Можете ли вы объяснить различия между этими двумя конструкциями и в каких случаях следует использовать каждую из них? Спасибо!

5 ответ(ов)

1

Я согласен с ответом @Bert F, но вот как я это понимаю.

У меня есть объект X в руках. Если я хочу записать свой X в список, этот список должен быть либо списком X, либо списком объектов, к которым мой X может быть приведен при добавлении, т.е. любым суперклассом X...

List<? super X>

Если я получаю список и хочу прочитать X из этого списка, лучше, чтобы это был список X или список объектов, которые могут быть приведены к X при чтении, т.е. любым объектом, который расширяет X...

List<? extends X>
0

Вам нужно визуализировать разницу между List<? extends T> и List<? super T>. Рассмотрим пример с классами:

class A { }
class B extends A { }
class C extends B { }

List<? extends T> — чтение и присваивание:

|-------------------------|-------------------|---------------------------------|
|         подстановочный знак        |        чтение        |              присваивание             |
|-------------------------|-------------------|---------------------------------|
|    List<? extends C>    |    A    B    C    |                       List<C>   |
|-------------------------|-------------------|---------------------------------|
|    List<? extends B>    |    A    B         |             List<B>   List<C>   |
|-------------------------|-------------------|---------------------------------|
|    List<? extends A>    |    A              |   List<A>   List<B>   List<C>   |
|-------------------------|-------------------|---------------------------------|

List<? super T> — запись и присваивание:

|-------------------------|-------------------|-------------------------------------------|
|         подстановочный знак        |        добавление        |                   присваивание                  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super C>     |              C    |  List<Object>  List<A>  List<B>  List<C>  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super B>     |         B    C    |  List<Object>  List<A>  List<B>           |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super A>     |    A    B    C    |  List<Object>  List<A>                    |
|-------------------------|-------------------|-------------------------------------------|

Важные моменты:

  • Всегда можно получить элемент типа Object из списка, независимо от подстановочного знака.
  • Всегда можно добавить null в изменяемый список, независимо от подстановочного знака.
0

Вопрос о том, как использовать ограниченные подстановочные знаки в Java может быть довольно запутанным, особенно когда речь идет о различиях между "super" и "extends".

В соответствии с материалом по Java Generics (например, из учебника Oracle), "super" обозначает нижнюю границу для обобщенных типов, а "extends" — верхнюю границу.

Синтаксис "? super T" указывает на неизвестный тип, который является суперклассом T (или самим T; не забывайте, что отношение суперкласса рефлексивно). Это позволяет вам принимать типы, которые находятся выше T в иерархии наследования.

В противоположность этому, "extends" используется для обозначения верхней границы. Когда вы пишете "? extends T", это означает, что вы работаете с типом, который является подклассом T.

Таким образом, использование "? super T" удобно в тех случаях, когда вам нужно записывать в структуру данных, поскольку вы можете добавлять подтипы T и любой из их суперклассов. А "? extends T" лучше подходит для чтения данных, так как это гарантирует, что вы сможете работать только с подтипами T.

Надеюсь, это проясняет различия между ними!

0

Когда вы работаете с списками в Java, важно понимать разницу между List<? extends X> и List<? super X> в контексте добавления и получения элементов.

Добавление элемента в список:

  • List<? extends X> не позволяет добавлять ничего, кроме null, в список. Это связано с тем, что компилятор не знает, какого конкретного типа объекты содержит список, так как он может содержать какие угодно подтипы X.

  • List<? super X> позволяет добавлять объекты, которые являются X или его подтипами, включая null. Здесь компилятор знает, что вы можете добавлять элементы определенного типа, поскольку он может гарантировать, что список принимает экземпляры этого типа.

Получение элемента из списка:

  • Когда вы получаете элемент из List<? extends X>, вы можете присвоить его переменной типа X или любому супертипу X, включая Object. Это возможно, так как вы точно знаете, что элемент будет неким типом, который является подтипом X.

  • Однако когда вы получаете элемент из List<? super X>, вы можете присвоить его только переменной типа Object, поскольку компилятор не знает, какого конкретного типа элемент находится в списке.

Примеры:

List<? extends Number> list1 = new ArrayList<Integer>();
list1.add(null);  // OK
Number n = list1.get(0);  // OK
Serializable s = list1.get(0);  // OK
Object o = list1.get(0);  // OK

list1.add(2.3);  // ERROR
list1.add(5);  // ERROR
list1.add(new Object());  // ERROR
Integer i = list1.get(0);  // ERROR
List<? super Number> list2 = new ArrayList<Number>();
list2.add(null);  // OK
list2.add(2.3);  // OK
list2.add(5);  // OK
Object o = list2.get(0);  // OK

list2.add(new Object());  // ERROR
Number n = list2.get(0);  // ERROR
Serializable s = list2.get(0);  // ERROR
Integer i = list2.get(0);  // ERROR

Таким образом, обращение к типам с использованием wildcards (? extends и ? super) влияет на то, что вы можете добавлять в список и как вы можете извлекать из него значения.

0

Вы можете ознакомиться со всеми ответами выше, чтобы понять, почему использование .add() ограничено '<?>', '<? extends>' и частично '<? super>'.

Но вот краткое резюме, если вы хотите запомнить это и не хотите каждый раз искать ответ:

List<? extends A> означает, что этот список будет принимать любой List типа A и его подклассов. Однако вы не можете добавлять в этот список ничего, даже объекты типа A.

List<? super A> означает, что этот список будет принимать любой список типа A и суперклассов A. Вы можете добавлять в этот список объекты типа A и его подклассы.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь