【问题标题】:Java Inheritance doubt in parameterised collection参数化集合中的Java继承疑问
【发布时间】:2010-06-07 17:43:47
【问题描述】:

很明显,父类的对象可以持有对子类的引用,但在参数化集合的情况下这不成立吗??

例如:

Car class is parent of Sedan

所以

public void doSomething(Car c){
    ...
}

public void caller(){
    Sedan s = new Sedan();
    doSomething(s);
}

显然是有效的

但是

public void doSomething(Collection<Car> c){
    ...
}

public void caller(){
    Collection<Sedan> s = new ArrayList<Sedan>();
    doSomething(s);
}

编译失败

有人可以指出原因吗?以及如何实现这样一个场景,其中一个函数需要遍历父对象的集合,只修改父类中存在的字段,使用父类方法,但调用方法(比如 3 个不同的方法)传递三种不同的亚型..

当然,如果我这样做,它编译得很好:

public void doSomething(Collection<Car> c){
    ...
}

public void caller(){
    Collection s = new ArrayList<Sedan>();
    doSomething(s);
}

【问题讨论】:

    标签: java generics inheritance


    【解决方案1】:

    使用

    public void doSomething(Collection<? extends Car> c){}
    

    或(按照建议)

    public <T extends Car> void doSomething(Collection<T> c){}
    

    这意味着CollectionCar(或Car 本身)的任何子类,而不是“它只是Car 实例的集合”

    这是因为集合是不变的,不像数组是协变的。引用Effective Java

    协变[..]表示如果SubSuper的子类型,那么数组类型Sub[]Super[]的子类型。相比之下,泛型是不变的:对于任何两个不同的类型Type1Type2List&lt;Type1&gt; 既不是List&lt;Type2&gt; 的子类型也不是超类型。

    【讨论】:

    • 如果你知道集合是同构的,那么写方法签名的更好的方法是 public <t extends car> void doSomething(Collection<t> c)</t></t>跨度>
    【解决方案2】:

    doSomething 需要声明为doSomething(Collection&lt;? extends Car&gt; c)。以这种方式声明,您将无法向集合中添加任何元素,因为您不知道该集合应该包含 Car 的哪个特定子类。

    这里的一般问题是Collection&lt;Sedan&gt; 根本不能被视为Collection&lt;Car&gt; 的子类,因为您不能在Collection&lt;Sedan&gt; 上执行您在Collection&lt;Car&gt; 上可以执行的所有操作。例如,您可以将addSportsCar 转换为Collection&lt;Car&gt;,因为SportsCarCar。您不能将SportsCar 添加到Collection&lt;Sedan&gt;,因为SportsCar 不是Sedan

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多