首页 文章

差异<?超级T>和<?在Java中扩展T> [重复]

提问于
浏览
624

这个问题在这里已有答案:

List<? super T>List<? extends T> 之间有什么区别?

我曾经使用 List<? extends T> ,但它不允许我向它添加元素 list.add(e) ,而 List<? super T> 则允许 .

15 回答

  • 17

    延伸

    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
    
    • Reading - 鉴于上述可能的分配,您保证从 List foo3 读取什么类型的对象:

    • 您可以读取 Number ,因为可以分配给 foo3 的任何列表都包含 NumberNumber 的子类 .

    • 您无法读取 Integer 因为 foo3 可能指向 List<Double> .

    • 您无法读取 Double 因为 foo3 可能指向 List<Integer> .

    • Writing - 鉴于上述可能的分配,您可以向 List foo3 添加哪种类型的对象,这对于 all 上述可能的 ArrayList 赋值是合法的:

    • 您无法添加 Integer ,因为 foo3 可能指向 List<Double> .

    • 您无法添加 Double ,因为 foo3 可能指向 List<Integer> .

    • 您无法添加 Number ,因为 foo3 可能指向 List<Integer> .

    您无法向 List<? extends T> 添加任何对象,因为您无法保证它实际指向的是哪种 List ,因此您无法保证该对象在该 List 中被允许 . 唯一的"guarantee"是你只能读取它,你将获得 TT 的子类 .

    超级

    现在考虑 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
    
    • Reading - 鉴于上述可能的分配,当您从 List foo3 读取时,您保证会接收哪种类型的对象:

    • 您无法保证 Integer 因为 foo3 可能指向 List<Number>List<Object> .

    • 您无法保证 Number 因为 foo3 可能指向 List<Object> .

    • only 保证您将获得 Object 的实例或 Object 的子类(但您不知道是什么子类) .

    • Writing - 鉴于上述可能的分配,您可以向 List foo3 添加哪种类型的对象,这对于 all 上述可能的 ArrayList 赋值是合法的:

    • 您可以添加 Integer ,因为上述任何列表中都允许使用 Integer .

    • 您可以添加 Integer 的子类的实例,因为在上述任何列表中都允许使用 Integer 的子类的实例 .

    • 您无法添加 Double ,因为 foo3 可能指向 ArrayList<Integer> .

    • 您无法添加 Number ,因为 foo3 可能指向 ArrayList<Integer> .

    • 您无法添加 Object ,因为 foo3 可能指向 ArrayList<Integer> .

    PECS

    记住PECS: "Producer Extends, Consumer Super" .

    • "Producer Extends" - 如果您需要 List 来生成 T 值(您希望从列表中读取 T s),则需要使用 ? extends T 声明它,例如: List<? extends Integer> . 但您无法添加到此列表中 .

    • "Consumer Super" - 如果您需要 List 来消耗 T 值(您希望将 T 写入列表中),则需要使用 ? super T 声明它,例如: List<? super Integer> . 但是无法保证您可以从此列表中读取哪种类型的对象 .

    • 如果您需要同时读取和写入列表,则需要在没有通配符的情况下完全声明它,例如 List<Integer> .

    示例

    注意this example from the 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)); 
      } 
    }
    

    另见How can I add to List<? extends Number> data structures?

  • 183

    想象一下这个层次结构

    enter image description here

    1.延伸

    通过写作

    List<? extends C2> list;
    

    你是说 list 将能够引用类型的对象(例如) ArrayList ,其泛型类型是 subtypessubtypes 之一(包括 C2 ):

    • C2: new ArrayList<C2>(); ,(可以存储C2或子类型的对象)或

    • D1: new ArrayList<D1>(); ,(可以存储D1或子类型的对象)或

    • 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
    

    对于每种可能的情况,我们有一组“可存储”类型:此处以图形方式表示7(红色)集

    enter image description here

    如您所见,并非每种情况都有一种安全类型:

    • 你不能 list.add(new C2(){}); 因为它可能是 list = new ArrayList<D1>();

    • 你不能 list.add(new D1(){}); 因为它可能是 list = new ArrayList<D2>();

    等等 .

    2.超级

    通过写作

    List<? super C2> list;
    

    你是说 list 将能够引用类型的对象(例如) ArrayList ,其泛型类型是 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{}
    

    之一(包括 C2 ):

    • A1: new ArrayList<A1>(); ,(可以存储A1或子类型的对象)或

    • A2: 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
    

    对于每种可能的情况,我们有一组“可存储”类型:此处以图形方式表示7(红色)集

    enter image description here

    如您所见,这里我们有七种安全类型,每种情况都是通用的: C2D1D2E1E2E3E4 .

    • 你可以 list.add(new C2(){}); 因为,无论我们引用哪种List,都允许 C2

    • 你可以 list.add(new D1(){}); 因为,无论我们引用哪种List,都允许 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{}
    
  • 1

    我喜欢@Bert F的答案,但这是我大脑看到的方式 .

    我手里拿着一个X.如果我想将 write 我的X放入一个List,那么该List必须是X的List或者我可以将其编写为X的事物列表,因为我将它们写入,即任何 superclass 的X ...

    List<? super   X>
    

    如果我得到一个List并且我希望 read 是该列表中的X,那么最好是X的列表或者当我读出它时可以向上转换为X的事物列表,即任何 extends X

    List<? extends X>
    

    希望这可以帮助 .

  • 1

    基于Bert F's answer我想解释一下我的理解 .

    假设我们有3个 class

    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.
    

    好的,现在让我们尝试从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.
    

    添加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.
    

    But wait 如果有人决定制作一种新型的柠檬,可以说是为了争论SaltyLemon

    public class SaltyLemon extends Lemon{}
    

    现在fruitExtendedList可以是Fruit,Melon,WaterMelon或SaltyLemon的列表 .

    所以,我们的声明

    fruitExtendedList.add(new WaterMelon())
    

    也无效 .

    Basically we can say that we cannot write anything to a fruitExtendedList.

    This sums up 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
    

    同样,Melon,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
    

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

    To sum it up we can add Melon or its subclass in melonSuperList and read only Object type object.

  • 1

    super是下限,extends是上限 .

    根据http://download.oracle.com/javase/tutorial/extra/generics/morefun.html

    解决方案是使用一种我们尚未见过的有界通配符形式:具有下限的通配符 . 语法? super T表示一个未知类型,它是T的超类型(或T本身;记住超类型关系是自反的) . 这是我们使用的有界通配符的双重性,我们在哪里使用?扩展T表示一个未知类型,它是T的子类型 .

  • 6

    我想想象一下这种差异 . 假设我们有:

    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 .

    • 无论通配符如何,您都可以始终 add null 到一个可变列表 .

  • 3

    使用 extends ,您只能从集合中获取 . 你不能投入其中 . 此外,虽然 super 允许get和put,但get期间的返回类型是 ? super T .

  • 14

    这里最令人困惑的是,无论我们指定什么类型限制,赋值只能以一种方式工作:

    baseClassInstance = derivedClassInstance;
    

    您可能认为 Integer extends NumberInteger 将作为 <? extends Number> ,但编译器会告诉您 <? extends Number> cannot be converted to Integer (也就是说,用人的说法,扩展数字的任何内容都可以转换为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 的任何超类(而不是因为 IntegerNumber 的超类,这不是真的) .

    EDIT添加了关于Consumer Extends和Producer Super的评论 - 它们没有意义,因为它们相应地指定了什么,只是 Object . 建议您记住PECS,因为CEPS永远不会有用 .

  • 1182

    通用通配符针对两个主要需求:

    从泛型集合中读取插入到泛型集合中有三种方法可以使用通用通配符定义集合(变量) . 这些是:

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

    List<?> 表示键入未知类型的列表 . 这可能是 List<A>List<B>List<String> 等 .

    List<? extends A> 表示作为 class Asubclasses of A (例如B和C)实例的对象列表 . List<? super A> 表示该列表键入 A classsuperclass of A .

    阅读更多:http://tutorials.jenkov.com/java-generics/wildcards.html

  • 1

    When to use extends and super

    通配符在方法参数中最有用 . 它们允许方法接口具有必要的灵活性 .

    人们经常在使用扩展时和使用超边界时感到困惑 . 经验法则是获取原则 . 如果从参数化容器中获取内容,请使用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将车辆放入估价师 . 知道扩展边界比超级更常见是有用的 .

  • 1

    您可以通过上面的所有答案来理解为什么 .add() 被限制为 '<?>''<? extends>' ,部分限制为 '<? super>' .

    但是如果你想记住它,并且不想每次都去探索答案,那么这就是结论:

    List<? extends A> 表示这将接受 ListAA 的子类 . 但是您无法在此列表中添加任何内容 . 甚至不是 A 类型的对象 .

    List<? super A> 表示这将接受 A 的任何列表和 A 的超类 . 您可以添加 A 类型的对象及其子类 .

  • 0

    最高投票的答案涵盖了许多方面的细节 . 但是,我会尝试以不同的方式回答这个问题 .

    我们需要考虑两件事,

    1.分配给列表变量

    List<? extends X> listvar;

    这里,任何 list of X or list of subclasses of X can be assigned 到listvar .

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


    List<? super X> listvar;

    这里,任何 list of X or list of superclasses of X can be assigned 到listvar .

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

    2.对列表变量执行读取或写入操作

    `List<? extends X> listvar;`
    

    您可以使用此功能接受方法参数中的列表,并对 type X 执行任何操作(注意:您可以从列表中 only read objects of type X ) .

    `List<? super Number> listvar;
    

    您可以使用此功能接受方法参数中的列表,并在 type Object 上执行任何操作,因为您可以从列表中选择 only read objects of type Object . But yes, additional thing here is , you can add objects of type X into the list.

  • 2

    List< ? extends X > 不允许在列表中添加除null之外的任何内容 .

    List< ? super X > 允许添加任何-X(X或其子类)或null .

  • 62

    例如,继承顺序假定为O> S> T> U> V.

    Using extends Keyword ,

    Correct:

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

    InCorrect:

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

    super Keyword:

    Correct:

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

    InCorrect:

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

    添加对象:List Object = new List();

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

    但为什么错误呢?让我们看一下列表对象初始化的可能性

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

    如果我们使用Object.add(new T());那么只有当它是正确的

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

    但还有两种可能性

    List Object = new List(); List Object = new List();如果我们尝试将(new T())添加到上述两种可能性中,它将产生错误,因为T是U和V的优等级 . 我们尝试将一个T对象[new(new T())]添加到U和V类型的List中 . 较高的类对象(基类)不能传递给较低的类Object(Sub类) .

    由于额外的两种可能性,即使你使用正确的可能性,Java也会给你错误,因为Java不知道你指的是什么对象 . 所以你不能将对象添加到List Object = new List();因为有可能无效 .

    添加对象: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
    

    但为什么上述两个错误发生?我们可以使用Object.add(new T());只有以下可能性,

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

    如果我们试图在List Object = new List()中使用Object.add(new T());和List Object = new List();然后它会给出错误这是因为我们不能将T对象[这是新的T()]添加到List Object = new List();因为它是U型的对象 . 我们不能将一个T对象[它是新的T()]添加到U对象,因为T是基类而U是子类 . 我们不能将基类添加到子类,这就是错误发生的原因 . 对于另一个案例,这是相同的 .

  • 0
    |
                                           v
    

    列表<?延伸X>?可以是X的任何子类或其自身X. List <?超级X>?可以是X的任何超类或其自身X.

    ^
                                          |
    

相关问题