Разница между

этот вопрос уже есть ответ здесь:

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

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

15 ответов


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>.
    • вы не можете читать Double, потому что foo3 может указывать на List<Integer>.
  2. писать - учитывая вышеизложенные возможные назначения, какой тип объекта вы могли бы добавить в List foo3 Это было бы законно для все возможные выше ArrayList задачи:

    • вы не можете добавить Integer, потому что foo3 может указывайте на List<Double>.
    • вы не можете добавить Double, потому что foo3 может указывать на List<Integer>.
    • вы не можете добавить Number, потому что foo3 может указывать на 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>.
    • вам не гарантируется Number, потому что foo3 может указывать на List<Object>.
    • на только гарантия заключается в том, что вы получите экземпляр Object или подкласс Object (но вы не знаете, что подкласс).
  2. писать - учитывая вышеизложенные возможные назначения, какой тип объекта можно добавить в List foo3 Это было бы законно для все возможные выше ArrayList задачи:

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

Печ

помните Печ: "Производитель Расширяется, Потребитель Супер".

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

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

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

пример

Примечание этот пример из Java Generics FAQ. Обратите внимание, как исходный список src (список производителей) использует extends, и пунктом списка dest (список потребления) использует 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)); 
  } 
}

Также см. как добавить в список расширяет число> структуры данных?


представьте себе эту иерархию

enter image description here

1. Удлиняет

писать

    List<? extends C2> list;

ты говоришь, что list будет ссылаться на объект типа (например) ArrayList чей общий тип является одним из 7 подтипов of C2 (C2 входит в комплект):

  1. C2: new ArrayList<C2>();, (объект, который может хранить C2 или подтипы) или
  2. D1: new ArrayList<D1>();, (объект, который может хранить D1 или подтипы) или
  3. D2: new ArrayList<D2>();, (объект, который может хранить D2 или подтипы) или...

и так далее. Семь разных случаев:

    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             

у нас есть набор типов "storable" для каждого возможного случая: 7 (красных) наборов здесь графически представлены

enter image description here

как вы можете видеть, нет безопасный тип это общее для каждого случая:

  • нельзя list.add(new C2(){}); потому что это может быть list = new ArrayList<D1>();
  • нельзя list.add(new D1(){}); потому что это может быть list = new ArrayList<D2>();

и так далее.

2. Супер!--43-->

писать

    List<? super C2> list;

ты говоришь, что list будет ссылаться на объект типа (например) ArrayList чей общий тип является одним из 7 супертипы из C2 (C2 входит в комплект):

  • А1: new ArrayList<A1>();, (объект, который может хранить A1 или подтипы) или
  • А2: new ArrayList<A2>();, (объект, который может хранить A2 или подтипы) или
  • A3: new ArrayList<A3>();, (объект, который может хранить A3 или подтипы) или...

и так далее. Семь разных случаев:

    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

у нас есть набор типов "storable" для каждого возможного случая: 7 (красный) наборы здесь графически представлены

enter image description here

как вы можете видеть, здесь у нас семь безопасных видов, которые являются общими для каждого случая: C2, D1, D2, E1, E2, E3, E4.

  • вы можете list.add(new C2(){}); потому что, независимо от вида списка, на который мы ссылаемся,C2 разрешено
  • вы можете list.add(new D1(){}); потому что, независимо от вида списка, на который мы ссылаемся, D1 разрешено

и так далее. Вероятно, вы заметили, что эти типы соответствуют иерархии, начиная с типа C2.

Примечания

здесь полная иерархия, если вы хотите сделать некоторые тесты

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{}

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

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

List<? super   X>

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

List<? extends X>

надеюсь, что это помогает.


на основе ответ Берта Ф я хотел бы объяснить мое понимание.

допустим у нас есть 3 класса как

public class Fruit{}

public class Melon extends Fruit{}

public class WaterMelon extends Melon{}

здесь мы имеем

List<? extends Fruit> fruitExtendedList = …

//Says that I can be a list of any object as long as this object extends Fruit.

Ok теперь давайте попробуем получить некоторое значение из fruitExtendedList

Fruit fruit = fruitExtendedList.get(position)

//This is valid as it can only return Fruit or its subclass.

еще раз попробуем

Melon melon = fruitExtendedList.get(position)

//This is not valid because fruitExtendedList can be a list of Fruit only, it may not be 
//list of Melon or WaterMelon and in java we cannot assign sub class object to 
//super class object reference without explicitly casting it.

то же самое относится к

WaterMelon waterMelon = fruitExtendedList.get(position)

теперь давайте попробуем установить какой-то объект в fruitExtendedList

добавлять фрукты объект

fruitExtendedList.add(new Fruit())

//This in not valid because as we know fruitExtendedList can be a list of any 
//object as long as this object extends Fruit. So what if it was the list of  
//WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.

добавление объекта дыни

fruitExtendedList.add(new Melon())

//This would be valid if fruitExtendedList was the list of Fruit but it may 
//not be, as it can also be the list of WaterMelon object. So, we see an invalid 
//condition already.

наконец, давайте попробуем добавить объект арбуз

fruitExtendedList.add(new WaterMelon())

//Ok, we got it now we can finally write to fruitExtendedList as WaterMelon 
//can be added to the list of Fruit or Melon as any superclass reference can point 
//to its subclass object.

но ждать что, если кто-то решит сделать новый тип лимона, скажем, для Аргументов sake SaltyLemon as

public class SaltyLemon extends Lemon{}

теперь fruitExtendedList может быть список фруктов, дыни, арбуза или SaltyLemon.

Итак, наше заявление

fruitExtendedList.add(new WaterMelon())

не действительный либо.

в основном мы можем сказать, что мы ничего не можем написать fruitExtendedList.

это подводит итог List<? extends Fruit>

теперь давайте посмотрим

List<? super Melon> melonSuperList= …

//Says that I can be a list of anything as long as its object has super class of Melon.

теперь давайте попробуем получить некоторое значение от melonSuperList

Fruit fruit = melonSuperList.get(position)

//This is not valid as melonSuperList can be a list of Object as in java all 
//the object extends from Object class. So, Object can be super class of Melon and 
//melonSuperList can be a list of Object type

аналогично дыня, арбуз или любой другой объект не может быть прочитан.

но обратите внимание, что мы можем читать тип объекта примеры

Object myObject = melonSuperList.get(position)

//This is valid because Object cannot have any super class and above statement 
//can return only Fruit, Melon, WaterMelon or Object they all can be referenced by
//Object type reference.

теперь давайте попробуем установить некоторое значение из melonSuperList.

добавление объекта типа object

melonSuperList.add(new Object())

//This is not valid as melonSuperList can be a list of Fruit or Melon.
//Note that Melon itself can be considered as super class of Melon.

добавление объекта типа фруктов

melonSuperList.add(new Fruit())

//This is also not valid as melonSuperList can be list of Melon

добавление объекта типа дыни

melonSuperList.add(new Melon())

//This is valid because melonSuperList can be list of Object, Fruit or Melon and in 
//this entire list we can add Melon type object.

добавление объекта типа арбуза

melonSuperList.add(new WaterMelon())

//This is also valid because of same reason as adding Melon

чтобы подвести итог, мы можем добавить дыню или ее подкласс в melonSuperList и прочитать только объект типа object.


super-нижняя граница, а extends-верхняя граница.

по данным http://download.oracle.com/javase/tutorial/extra/generics/morefun.html :

решение заключается в использовании форма ограниченный подстановочный знак, который мы еще не видели: маски с нижней границей. Этот синтаксис ? super T обозначает неизвестное тип, который является супертипом T (или T сам; помните, что супертип отношение рефлексивно). Это двойной ограниченных маски мы использование, где мы используем ? удлиняет Т обозначить неизвестный тип, который является подтип т.


используя выходит вы можете получить только из коллекции. Вы не можете вложить в него. Кроме того, хотя супер позволяет как получить, так и положить, тип возврата во время get ? super T.


самое запутанное здесь то, что какие бы ограничения типа мы не указывали, назначение работает только одним способом:

baseClassInstance = derivedClassInstance;

вы можете подумать, что Integer extends Number и Integer будет <? extends Number>, но компилятор скажет вам, что <? extends Number> cannot be converted to Integer (то есть, на человеческом языке,это неправильно, что все, что расширяет число может быть преобразовано в целое число):

class Holder<T> {
    T v;
    T get() { return v; }
    void set(T n) { v=n; }
}
class A {
    public static void main(String[]args) {
        Holder<? extends Number> he = new Holder();
        Holder<? super Number> hs = new Holder();

        Integer i;
        Number n;
        Object o;

        // Producer Super: always gives an error except
        //       when consumer expects just Object
        i = hs.get(); // <? super Number> cannot be converted to Integer
        n = hs.get(); // <? super Number> cannot be converted to Number
                      // <? super Number> cannot be converted to ... (but
                      //       there is no class between Number and Object)
        o = hs.get();

        // Consumer Super
        hs.set(i);
        hs.set(n);
        hs.set(o); // Object cannot be converted to <? super Number>

        // Producer Extends
        i = he.get(); // <? extends Number> cannot be converted to Integer
        n = he.get();
        o = he.get();

        // Consumer Extends: always gives an error
        he.set(i); // Integer cannot be converted to <? extends Number>
        he.set(n); // Number cannot be converted to <? extends Number>
        he.set(o); // Object cannot be converted to <? extends Number>
    }
}

hs.set(i); это нормально, потому что Integer может быть преобразован в любой суперкласс Number (и не потому что Integer - это суперкласс Number, что неверно).

EDIT добавлен комментарий о Consumer Extends и Producer Super -- они не имеют смысла, потому что они указывают, соответственно, ничего и просто Object. Вам советуют помнить о грудных мышцах, потому что белые грибы никогда не полезны.


общие подстановочные знаки предназначены для двух основных потребностей:

чтение из общей коллекции Вставка в общую коллекцию Существует три способа определения коллекции (переменной) с помощью общих подстановочных знаков. Это:

List<?>           listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super   A> listUknown = new ArrayList<A>();

List<?> означает список, типизированный для неизвестного типа. Это может быть List<A>, a List<B>, a List<String> etc.

List<? extends A> значит, список объектов, которые являются экземплярами class A или subclasses of A (например, B и C). List<? super A> это означает, что список набирается либо A class или superclass of A.

Подробнее:http://tutorials.jenkov.com/java-generics/wildcards.html


когда использовать extends и super

маски наиболее полезны в параметрах метода. Они учитывают необходимую гибкость в интерфейсах метода.

люди часто путают, когда использовать extends и когда использовать супер границы. Эмпирическое правило-это принцип get-put. Если вы получаете что-то из параметризованного контейнера, используйте extends.

int totalFuel(List<? extends Vehicle> list) {
int total = 0;
for(Vehicle v : list) {
    total += v.getFuel();
}
return total;}

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

int totalValue(Valuer<? super Vehicle> valuer) {
int total = 0;
for(Vehicle v : vehicles) {
    total += valuer.evaluate(v);
}
return total;}

метод totalValue помещает транспортные средства в оценщик. Полезно знать, что extends bound гораздо более распространен, чем super.


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

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

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

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


up проголосовали ответы охватывает детали по многим аспектам. Однако я постараюсь ответить на этот вопрос по-другому.

есть 2 вещи, которые мы должны рассмотреть,

1. Присвоение переменной списка

List<? extends X> listvar;

здесь любая список X или список подклассов X может быть назначен в listvar.

List<? extends Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();


List<? super X> listvar;

здесь любая список X или список суперклассов X может быть назначено в listvar.

List<? super Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();

2. Выполните операцию чтения или записи в переменной списка

`List<? extends X> listvar;`

вы можете использовать эту функцию, чтобы принять список в аргументах метода и выполнить любые операции над тип X (Примечание: Вы можете читать только объекты типа X из списка).

`List<? super Number> listvar;

вы можете использовать эту функцию, чтобы принять список в аргументах метода и выполнить любые операции на тип как вы можете читать только объекты типа Object из списка. Но да, дополнительная вещь здесь, вы можете добавить объекты типа X в список.


список не позволяет добавлять в список ничего, кроме null.

список позволяет добавлять все, что есть-X (X или его подкласс) или null.


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

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>                     |
|-------------------------|-------------------|--------------------------------------------|

во всех случаях:

  • вы всегда можете get Object из списка независимо от подстановочного знака.
  • вы всегда можете добавить null в изменяемый список независимо от подстановочного знака.

                                       |
                                       v

список ? может быть любой подкласс of X и сам X.

список ? может быть любой суперкласса of X и сам X.

                                      ^
                                      |

пример, Порядок наследования принимается как O > S > T > U > V

используя ключевое слово extends ,

правильно:

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

неправильно:

List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();

супер сайта:

правильно:

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

неправильно:

List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();

Добавить объект: List Object = новый список ();

Object.add(new T()); //error

но почему ошибка ? Давайте рассмотрим возможности инициализации объекта List

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

если мы используем объект.добавить (new T ()); тогда это будет правильно, только если

List<? extends T> Object = new List<T>(); 

но есть дополнительные две возможности

список Объект = новый список(); List Object = новый список(); Если мы попытаемся добавить (новый T ()) к вышеупомянутым двум возможностям, это даст ошибку, потому что T-высший класс U и V . мы пытаемся добавить объект T [который является (new T ())] в список типов U и V . Объект более высокого класса(базовый класс) не может быть передан объекту более низкого класса (подклассу).

из-за дополнительных двух возможностей Java дает вам ошибку, даже если вы используете правильное равновесие, поскольку Java не знает, какой объект вы имеете в виду - ... Таким образом, вы не можете добавлять объекты в List Object = new List(); поскольку есть возможности, которые недопустимы.

Добавить объект: List Object = новый список ();

Object.add(new T()); // compiles fine without error
Object.add(new U()); // compiles fine without error
Object.add(new V()); // compiles fine without error

Object.add(new S()); //  error
Object.add(new O()); //  error

но почему ошибка возникает в вышеупомянутых двух ? мы можем использовать объект.добавить (new T ()); только по нижеприведенным возможностям,

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

если мы попытаемся использовать Object.добавить(новый T()) в List Object = новый список(); и List Object = новый список(); тогда это даст ошибку Это потому что Мы не можем добавить T объект[который является новым T ()] для объекта List = new List (); потому что это объект типа U . Мы не можем добавить объект T[который является новым T()] к объекту U, потому что T-базовый класс, а U-подкласс . Мы не можем добавить базовый класс подкласс и поэтому возникает ошибка . То же самое в другом случае .