Вопрос-ответ

Difference between <? super T> and <? extends T> in Java [duplicate]

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

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

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

Переведено автоматически
Ответ 1

extends

Объявление подстановочного знака List<? extends Number> foo3 означает, что любое из этих назначений является законным:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number

  1. Чтение - Учитывая приведенные выше возможные назначения, из какого типа объекта вы гарантированно будете читатьList foo3:



    • Вы можете прочитать Number, потому что любой из списков, которым можно было бы присвоить значение foo3, содержит Number или подкласс Number.

    • Вы не можете прочитать, Integer потому что foo3 это может указывать на List<Double>.

    • Вы не можете прочитать a, Double потому что foo3 это может указывать на a List<Integer>.


  2. Запись - Учитывая вышеуказанные возможные назначения, к какому типу объекта вы могли бы добавить, List foo3 который был бы допустим для всех вышеуказанных возможных ArrayList назначений:



    • Вы не можете добавить Integer потому foo3 что это может указывать на List<Double>.

    • Вы не можете добавить a, Double потому что foo3 это может указывать на a List<Integer>.

    • Вы не можете добавить a, Number потому что foo3 это может указывать на a List<Integer>.


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

super

Теперь рассмотрим List <? super T>.

Объявление подстановочного знака List<? super Integer> foo3 означает, что любое из этих назначений является законным:

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

  1. Чтение - Учитывая приведенные выше возможные назначения, какой тип объекта вы гарантированно получите при чтении из List foo3:



    • Вам не гарантируется, Integer потому что foo3 это может указывать на List<Number> или List<Object>.

    • Вам не гарантируется, что a Number потому что foo3 может указывать на a List<Object>.

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


  2. Запись - Учитывая вышеуказанные возможные назначения, к какому типу объекта вы могли бы добавить, List foo3 который был бы допустим для всех вышеуказанных возможных ArrayList назначений:



    • Вы можете добавить an, Integer потому что an Integer разрешен в любом из приведенных выше списков.

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

    • Вы не можете добавить a, Double потому что foo3 это может указывать на ArrayList<Integer>.

    • Вы не можете добавить a, Number потому что foo3 это может указывать на ArrayList<Integer>.

    • Вы не можете добавить an, Object потому что foo3 это может указывать на ArrayList<Integer>.


PECS

Remember PECS: "Producer Extends, Consumer Super".


  • "Producer Extends" - If you need a List to produce T values (you want to read Ts from the list), you need to declare it with ? extends T, e.g. List<? extends Integer>. But you cannot add to this list.


  • "Consumer Super" - If you need a List to consume T values (you want to write Ts into the list), you need to declare it with ? super T, e.g. List<? super Integer>. But there are no guarantees what type of object you may read from this list.


  • If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g. List<Integer>.


Example

Note this example from the Java Generics FAQ. Note how the source list src (the producing list) uses extends, and the destination list dest (the consuming list) uses super:

public class Collections { 
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}

Also see
How can I add to List<? extends Number> data structures?

Ответ 2

Imagine having this hierarchy

enter image description here

1. Extends

By writing

    List<? extends C2> list;

you are saying that list will be able to reference an object of type (for example) ArrayList whose generic type is one of the 7 subtypes of C2 (C2 included):


  1. C2: new ArrayList<C2>();, (an object that can store C2 or subtypes) or

  2. D1: new ArrayList<D1>();, (an object that can store D1 or subtypes) or

  3. D2: new ArrayList<D2>();, (an object that can store D2 or subtypes) or...

and so on. Seven different cases:

    1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
2) new ArrayList<D1>(): can store D1 E1 E2
3) new ArrayList<D2>(): can store D2 E3 E4
4) new ArrayList<E1>(): can store E1
5) new ArrayList<E2>(): can store E2
6) new ArrayList<E3>(): can store E3
7) new ArrayList<E4>(): can store E4

We have a set of "storable" types for each possible case: 7 (red) sets here graphically represented

enter image description here

As you can see, there is not a safe type that is common to every case:


  • you cannot list.add(new C2(){}); because it could be list = new ArrayList<D1>();

  • you cannot list.add(new D1(){}); because it could be list = new ArrayList<D2>();

and so on.

2. Super

By writing

    List<? super C2> list;

you are saying that list will be able to reference an object of type (for example) ArrayList whose generic type is one of the 7 supertypes of C2 (C2 included):


  • A1: new ArrayList<A1>();, (an object that can store A1 or subtypes) or

  • A2: new ArrayList<A2>();, (an object that can store A2 or subtypes) or

  • A3: new ArrayList<A3>();, (an object that can store A3 or subtypes) or...

and so on. Seven different cases:

    1) new ArrayList<A1>(): can store A1          B1 B2       C1 C2    D1 D2 E1 E2 E3 E4
2) new ArrayList<A2>(): can store A2 B2 C1 C2 D1 D2 E1 E2 E3 E4
3) new ArrayList<A3>(): can store A3 B3 C2 C3 D1 D2 E1 E2 E3 E4
4) new ArrayList<A4>(): can store A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4
5) new ArrayList<B2>(): can store B2 C1 C2 D1 D2 E1 E2 E3 E4
6) new ArrayList<B3>(): can store B3 C2 C3 D1 D2 E1 E2 E3 E4
7) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4

We have a set of "storable" types for each possible case: 7 (red) sets here graphically represented

enter image description here

As you can see, here we have seven safe types that are common to every case: C2, D1, D2, E1, E2, E3, E4.


  • you can list.add(new C2(){}); because, regardless of the kind of List we're referencing, C2 is allowed

  • you can list.add(new D1(){}); because, regardless of the kind of List we're referencing, D1 is allowed

and so on. You probably noticed that these types correspond to the hierarchy starting from type C2.

Notes

Here the complete hierarchy if you wish to make some tests

interface A1{}
interface A2{}
interface A3{}
interface A4{}

interface B1 extends A1{}
interface B2 extends A1,A2{}
interface B3 extends A3,A4{}
interface B4 extends A4{}

interface C1 extends B2{}
interface C2 extends B2,B3{}
interface C3 extends B3{}

interface D1 extends C1,C2{}
interface D2 extends C2{}

interface E1 extends D1{}
interface E2 extends D1{}
interface E3 extends D2{}
interface E4 extends D2{}
Ответ 3

Мне нравится ответ от @Bert F, но мой мозг видит его именно так.

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

List<? super   X>

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

List<? extends X>
Ответ 4

Я хотел бы наглядно представить разницу. Предположим, у нас есть:

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

List<? extends T> - чтение и назначение:

|-------------------------|-------------------|---------------------------------|
| wildcard | get | assign |
|-------------------------|-------------------|---------------------------------|
| 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> - написание и назначение:

|-------------------------|-------------------|-------------------------------------------|
| wildcard | add | assign |
|-------------------------|-------------------|-------------------------------------------|
| 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 в изменяемый список независимо от подстановочного знака.

java generics collections