Разница между & lt ;? супер T & gt; и & lt ;? расширяет T & gt; на Яве

В чем разница между List и List ?

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

вопрос задан 3.12.2010
Anand
3686 репутация

15 ответов


  • 1130 рейтинг

    extends

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

    List
     foo3 = new ArrayList();  // Number "extends" Number (in this context)
    List
     foo3 = new ArrayList(); // Integer extends Number
    List
     foo3 = new ArrayList();  // Double extends Number
    
    1. Чтение - С учетом вышеупомянутых возможных назначений, какой тип объекта вы гарантированно читаете из List foo3:

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

      • Невозможно добавить Integer, поскольку foo3 может указывать на List.
      • Нельзя добавить Double, потому что foo3 может указывать на List.
      • Нельзя добавить Number, поскольку foo3 может указывать на List.

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

    super

    Теперь рассмотрим List .

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

    List
     foo3 = new ArrayList();  // Integer is a "superclass" of Integer (in this context)
    List
     foo3 = new ArrayList();   // Number is a superclass of Integer
    List
     foo3 = new ArrayList();   // Object is a superclass of Integer
    
    1. Чтение - Учитывая приведенные выше возможные назначения, какой тип объекта вы гарантированно получите, когда читаете из List foo3:

      • Вам не гарантирован Integer, потому что foo3 может указывать на List или List.
      • Вам не гарантировано Number, потому что foo3 может указывать на List.
      • только гарантирует, что вы получите экземпляр Object или подкласс Object (но вы не знаете, какой подкласс).
    2. Написание - Учитывая вышеупомянутые возможные назначения, какой тип объекта вы могли бы добавить к List foo3, который был бы допустим для для всех вышеупомянутых возможных ArrayList назначений:

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

    УИК

    Помните, PECS : "Продюсер продлевает, потребительский супер" .

    • «Продление производителя» - Если вам нужен List для получения T значений (вы хотите прочитать T с из списка), вам нужно объявить его с ? extends T, e. г. List . Но вы не можете добавить в этот список.

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

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

    Пример

    Обратите внимание на этот пример из часто задаваемых вопросов об универсальных элементах Java . Обратите внимание, что в списке источников src (производящий список) используется extends, а в списке адресатов dest (список потребления) используется super:

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

    Также см. Как добавить в список & lt ;? расширяет номер & gt; структуры данных?

    ответ дан Bert F, с репутацией 61257, 3.12.2010
  • 174 рейтинг

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

    enter image description here

    1. Удлиняет

    Письмо

        List
     list;
    

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

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

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

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

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

    enter image description here

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

    • вы не можете list.add(new C2(){});, потому что это может быть list = new ArrayList();
    • вы не можете list.add(new D1(){});, потому что это может быть list = new ArrayList();

    и так далее.

    2. Супер

    Письмо

        List
     list;
    

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

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

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

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

    У нас есть набор «хранимых» типов для каждого возможного случая: 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{}
    
    ответ дан Luigi Cortese, с репутацией 5886, 20.10.2015
  • 58 рейтинг

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

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

    List
    
    

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

    List
    
    

    Надеюсь, это поможет.

    ответ дан Michael Dausmann, с репутацией 2025, 3.12.2015
  • 17 рейтинг

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

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

    public class Fruit{}
    
    public class Melon extends Fruit{}
    
    public class WaterMelon extends Melon{}
    

    Здесь у нас есть

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

    Хорошо, теперь давайте попробуем получить значение из 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.
    

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

    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.
    

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

    public class SaltyLemon extends Lemon{}
    

    Теперь в списке «FruitExtendedList» можно указать список «Fruit», «Melon», «WaterMelon» или «SaltyLemon».

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

    fruitExtendedList.add(new WaterMelon())
    

    также недействителен.

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

    Это подводит итог List

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

    List
     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
    

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

    Но обратите внимание, что мы можем читать экземпляры типов объектов

    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.

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

    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.
    

    Добавление объекта типа Fruit

    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.
    

    Добавление объекта типа WaterMelon

    melonSuperList.add(new WaterMelon())
    
    //This is also valid because of same reason as adding Melon
    

    Чтобы подвести итог, мы можем добавить Melon или его подкласс в melonSuperList и только для чтения объект типа объекта.

    ответ дан Sushant, с репутацией 1308, 19.03.2016
  • 14 рейтинг

    супер - это нижняя граница, а расширение - это верхняя граница.

    Согласно http: // download. оракул. ком / JavaSE / учебник / дополнительные / генерики / morefun. HTML :

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

    ответ дан Istao, с репутацией 4308, 3.12.2010
  • 3 рейтинг

    Использование расширяет , вы можете получить только из коллекции. Вы не можете положить в это. Кроме того, хотя super позволяет как получить, так и поставить, тип возвращаемого значения во время получения равен ? Супер Т .

    ответ дан Sai Sunder, с репутацией 753, 28.01.2014
  • 2 рейтинг

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

    baseClassInstance = derivedClassInstance;
    

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

    class Holder {
        T v;
        T get() { return v; }
        void set(T n) { v=n; }
    }
    class A {
        public static void main(String[]args) {
            Holder
     he = new Holder();
            Holder
     hs = new Holder();
    
            Integer i;
            Number n;
            Object o;
    
            // Producer Super: always gives an error except
            //       when consumer expects just Object
            i = hs.get(); // 
     cannot be converted to Integer
            n = hs.get(); // 
     cannot be converted to 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 
    
    
            // Producer Extends
            i = he.get(); // 
     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 
    
            he.set(n); // Number cannot be converted to 
    
            he.set(o); // Object cannot be converted to 
    
        }
    }
    

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

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

    ответ дан 18446744073709551615, с репутацией 11204, 8.07.2016
  • 1 рейтинг

    Когда использовать расширяет и супер

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

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

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

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

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

    Метод totalValue помещает Транспортные средства в Оценщик. Полезно знать, что расширение границ гораздо чаще, чем супер.

    ответ дан Kevin STS, с репутацией 59, 1.06.2017
  • 1 рейтинг

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

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

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

    List listvar;

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

    List listvar; listvar = new ArrayList(); listvar = new ArrayList();


    List listvar;

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

    List listvar; listvar = new ArrayList(); listvar = new ArrayList();

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

    `List
     listvar;`
    

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

    `List
     listvar;
    

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

    ответ дан Shailesh Pratapwar, с репутацией 1936, 31.03.2018
  • 1 рейтинг

    Список & lt; ? расширяет X & gt; не позволяет добавлять ничего, кроме нуля, в список.

    Список & lt; ? супер х & gt; позволяет добавлять все, что есть - X (X или его подкласс) или нуль.

    ответ дан elyor, с репутацией 372, 28.09.2017
  • 1 рейтинг

    Вы можете просмотреть все ответы выше, чтобы понять, почему .add() ограничен ' ', ' ' и частично ' '.

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

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

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

    ответ дан jforex78, с репутацией 50, 22.08.2017
  • 1 рейтинг

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

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

    List
               listUknown = new ArrayList();
    List
     listUknown = new ArrayList();
    List
     listUknown = new ArrayList();
    

    List означает список, напечатанный с неизвестным типом. Это может быть List, List, List и т. Д.

    List означает список объектов, которые являются экземплярами class A или subclasses of A (например, г. Б и В). List означает, что список набирается либо A class, либо superclass of A.

    Подробнее: http: // tutorials. jenkov. ком / Java-генерики / шаблоны. HTML

    ответ дан Vaibhav Gupta, с репутацией 158, 23.08.2015
  • 1 рейтинг

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

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

    List - чтение и назначение:

    |-------------------------|-------------------|---------------------------------|
    |         wildcard        |        get        |              assign             |
    |-------------------------|-------------------|---------------------------------|
    |    List
        |    A    B    C    |   List                       |
    |-------------------------|-------------------|---------------------------------|
    |    List
        |    A    B         |   List   List             |
    |-------------------------|-------------------|---------------------------------|
    |    List
        |    A              |   List   List List | |-------------------------|-------------------|---------------------------------| 

    List - написание и назначение:

    |-------------------------|-------------------|--------------------------------------------|
    |         wildcard        |        add        |                   assign                   |
    |-------------------------|-------------------|--------------------------------------------|
    |     List
         |    C              |  List  List List List | |-------------------------|-------------------|--------------------------------------------| | List
     | B C | List List List | |-------------------------|-------------------|--------------------------------------------| | List
     | A B C | List List | |-------------------------|-------------------|--------------------------------------------| 

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

    • Вы всегда можете получить Object из списка независимо от подстановочного знака.
    • Вы всегда можете добавить null в изменяемый список независимо от подстановочного знака.
    ответ дан Oleksandr, с репутацией 7075, 23.09.2018
  • 0 рейтинг
                                           |
                                           v
    

    Список & lt; ? расширяет X & gt; ? может быть любым подклассом из X или самим X.

    Список & lt; ? супер х & gt; ? может быть любым суперклассом из X или самим X.

                                          ^
                                          |
    
    ответ дан snr, с репутацией 4821, 11.08.2018
  • -1 рейтинг

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

    Использование расширяет ключевое слово,

    Правильно:

    List
     Object = new List();
    List
     Object = new List();
    List
     Object = new List();
    

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

    List
     Object = new List();
    List
     Object = new List();
    

    super Ключевое слово:

    Правильно:

    List
     Object = new List();
    List
     Object = new List();
    List
     Object = new List();
    

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

    List
     Object = new List();
    List
     Object = new List();
    

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

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

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

    List
     Object = new List();
    List
     Object = new List();
    List
     Object = new List();
    

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

    List
     Object = new List(); 
    

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

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

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

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

    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
    

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

    List
     Object = new List();
    List
     Object = new List();
    List
     Object = new List();
    

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

    ответ дан Crox, с репутацией 1, 22.07.2018