1

我有以下课程

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<Object> objectListArrayList<String> stringList我们不能将 stringList 替换为 objList。这将导致编译错误

我的尝试是像在 Driver 类中一样使列表协变。

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

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

4

3 回答 3

4

我认为您误解了通用通配符 ( ?)。来自有关该主题的 Java 教程

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

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

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

Java 泛型集合不是协变的;参见例如Java 理论和实践:泛型陷阱以获得解释。

于 2012-07-14T14:07:09.907 回答
1

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

悲伤的答案,但真实。来自维基百科:

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

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

于 2012-07-14T14:07:26.983 回答
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());

这会正常工作。

于 2012-07-14T14:19:12.083 回答