如何制作java list co-variant

时间:2012-07-14 14:03:19

标签: java list subclass superclass invariants

我有以下课程

public class Animal


public class Dog extends Animal


public class Cat extends Animal

对于测试我有一个驱动程序类。

public class Driver {
    public static void main(String[] args){
        List<? extends Animal> animalList = Arrays.<Dog>asList(new Dog(), new Dog(), new Dog());
        animalList.add(new Dog()) // Compilation error 
    }               
}

默认情况下,list是不变类型的容器。例如,我们有List<Object> objectListArrayList<String> stringList我们无法将stringList替换为objList。这将导致编译错误

我的尝试是在Driver类中创建list co-variant。

根据<? extends Animal>,我们可以应用任何动物类型的对象,包括动物类型。

但是我在指定的行中遇到了编译问题。有人可以从理论上解释我哪里出错了。

3 个答案:

答案 0 :(得分:4)

我认为你误解了通用通配符(?)。来自Java tutorial on the subject

  

与往常一样,使用灵活性需要付出代价   通配符。这个价格是写入形状现在是非法的   在方法体内。例如,这是不允许的:

public void addRectangle(List<? extends Shape> shapes) {
    // Compile-time error!
    shapes.add(0, new Rectangle());
}
     

您应该能够弄清楚为什么不允许上面的代码。该   shapes.add()的第二个参数的类型是? extends Shape - an   未知的Shape子类型。由于我们不知道它是什么类型,我们   不知道它是否是Rectangle的超类型;它可能会也可能不会   这样的超类型,因此传递Rectangle是不安全的。

Java泛型集合不是协变的;见例如Java theory and practice: Generics gotchas获得解释。

答案 1 :(得分:1)

java没有一个非常强大的类型系统,这意味着:你不能这样做。

悲伤的回答,但是确实如此。来自维基百科:

  

与数组不同,泛型类既不是协变也不是   逆变。例如,List<String>List<Object>都不是   另一个

的子类型

http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Java

答案 2 :(得分:0)

更改您的代码:

public class Driver {
    public static void main(String[] args){
        List<? extends Animal> animalList = Arrays.<Cat>asList(new Cat(), new Cat(), new Cat());
        animalList.add(new Dog()) // Compilation error 
    }               
}

并且编译错误更有意义吗?所有Java都必须脱离你为animalList提供的类型。 Java知道右侧的东西可能是Cat个对象的列表,所以它不会让你放入Dog。将您的代码更改为

List<Animal> animalList = Arrays.<Animal>asList(new Dog(), new Dog(), new Dog());

这样就可以了。