为啥我不能将 B 的超类对象放入 Container<?超级B>? [复制]

Posted

技术标签:

【中文标题】为啥我不能将 B 的超类对象放入 Container<?超级B>? [复制]【英文标题】:Why can't I put an object of superclass of B into Container<? super B>? [duplicate]为什么我不能将 B 的超类对象放入 Container<?超级B>? [复制] 【发布时间】:2020-12-18 16:16:02 【问题描述】:

我有下面的代码。似乎我无法将作为 Vehicle 类超类的 Nonlife 类的对象放入 Collection&lt;? super Vehicle&gt; 类型的容器中 尽管 通配符中有一个关键字“super”类型,并且只有类 VehicleSUV 的对象是类 Vehicle 的子类是可行的。有人能给我一些建议吗?

public class SUV extends Vehicle

public class Vehicle extends Nonlife implements Externalizable

public class Nonlife extends Thing

public class Thing implements Comparable<Thing>, Serializable

public class SupperWildcardTest20200830 
    
    public static void main(String[] args) 
        Collection<Thing> coll = new ArrayList<>();
        appendVehicle2Collection(coll);
        appendSuv2Collection(coll);
        for (Thing el: coll) 
            System.out.println("" + el);
        
    
    
    public static void appendVehicle2Collection(Collection<? super Vehicle> coll) 
        coll.add(new Vehicle());
    
    
    public static void appendSuv2Collection(Collection<? super Vehicle> coll) 
        coll.add(new SUV());
    
    
    public static void appendNolife2Collection(Collection<? super Vehicle> coll) 
        /**
         * incompatible types: Nonlife cannot be converted to CAP#1
         *  where CAP#1 is a fresh type-variable:
         *    CAP#1 extends Object super: Vehicle from capture of ? super Vehicle
         */
        coll.add(new Nonlife());
    

【问题讨论】:

阅读此文可能有用:***.com/questions/4343202/… 【参考方案1】:

关于Collection&lt;? super Vehicle&gt;,你唯一确定的是它是一个Vehicles 的集合,或者是一个Vehicles 超类型的集合。因此,您唯一确定可以放入此系列的就是车辆。因此,您可以将一个非生命集合传递给该方法,但您仍然可以只将车辆或子类型放入该方法内的集合中。

一般来说:使用 super,您可以将提到的类型或子类型的值放入其中。使用扩展,您可以从集合中检索提到的类型,或将它们作为超类型检索。

【讨论】:

感谢您的回答。后来在我问了这个问题之后,我在 Core Java Edition 9 这本书中找到了一条语句:“直观地说,具有超类型边界的通配符允许您写入通用对象,而具有子类型边界的通配符允许您从通用对象中读取。” .目前,我只能像这些陈述(你的或书的)一般理解它。希望有一天我可以通过理解JVM和Java编译器来准确理解它。但我想这离现在还很远。【参考方案2】:

这是一个Wildcard Capture 问题。

TL;DR - 当您在 Generic Collection 类型定义中使用通配符(无论是 superextends)时,从该集合中获取元素并适当地转换它可以被认为是安全,而将元素添加到集合中则不是,并且出于安全目的而实现此机制。

让我们检查一下 Oracle 文档中给出的 example,它说明了为什么需要这种安全性的原因(此示例使用 extends,但同样的原则适用于 super):

代码:

import java.util.List;

public class WildcardErrorBad 

    void swapFirst(List<? extends Number> l1, List<? extends Number> l2) 
        Number temp = l1.get(0);
        l1.set(0, l2.get(0)); // expected a CAP#1 extends Number, got a CAP#2 extends Number;
        l2.set(0, temp); // expected a CAP#1 extends Number, got a Number
    

不编译,因为它正在尝试一个不安全的操作,因为,如果你将调用这个方法如下:

List<Integer> li = Arrays.asList(1, 2, 3);
List<Double>  ld = Arrays.asList(10.10, 20.20, 30.30);
swapFirst(li, ld);

虽然 List&lt;Integer&gt;List&lt;Double&gt; 都满足 List&lt;? extends Number&gt; 的条件,但从 Integer 值列表中取出项目并尝试将其放入 Double 值列表中显然是不正确的。

我喜欢的另一个例子是Jon Skeet,它看起来像this。

您可能还想阅读this。

【讨论】:

肯定 super Vehicle> 读作任何属于 Vehicle 超类的类型? 我只是说第一句话似乎误导了我。 Car 是一种扩展 Vehicle 的类型。据我了解, Car 不匹配 。很高兴被证明是错误的 @mattfreake 我实际上完全改变了我的(最初是错误的)答案。 嗨@GiorgiTsiklauri,感谢您的回答。但我这个问题的意图不是关于 SomeClass> 和辅助方法的通配符捕获,而是关于为什么不能将超类的对象设置为 SomeClass super T> 虽然通配符类型中有关键字“super”。

以上是关于为啥我不能将 B 的超类对象放入 Container<?超级B>? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

使用返回超类对象的超类方法 - Java

给定一个指向对象的超类指针,如何获取对象的大小?

Java多态如何调用子类对象的超类方法

如何在核心数据中以编程方式创建超类/子类?

具有子类数据的超类方法

如何从别处调用 Objective-C 对象的超类的方法?