Java泛型协变

26
我对以下文章有些困惑:http://www.ibm.com/developerworks/java/library/j-jtp01255.html 在“泛型不是协变的”下,作者提到:
因为ln是一个List,向其中添加一个Float似乎完全合法。但如果ln与li同名,则会打破定义li的隐含类型安全承诺——它是一个整数列表,这就是为什么泛型类型不能是协变的原因。
我不理解它说“如果ln与li同名”,作者什么意思?(是指引用吗?)引用的代码片段表明了Java中不合法的内容,但并没有说明为什么不合法。如果有人能举个例子来解释一下,那对我会非常有帮助。 谢谢。

如果我没记错的话,通用类型不是协变的,因为它们不能改变先前版本的Java中数组的实现/使用方式(向后兼容性),这对某些人来说有印象吗? - Rhangaun
@Skeptic, 数组是协变泛型实现的一个示例 - 它会抛出运行时异常。Java无法做到这一点,因为泛型在运行时被擦除,所以它只能通过限制协变来控制它。 - Yishai
@Yishai,有关数组的问题是由Steele在一次名为“Growing a Language”的演示中讨论的,但我现在似乎找不到了。 - Rhangaun
那个IBM的链接似乎已经失效了,但是在Wayback Machine上有一份副本:http://web.archive.org/web/20121104021805/http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html - wool.in.silver
@Skeptic,Steele演示的书面版本没有提到数组问题。然而,它是一篇非常好的阅读材料。您可以通过http://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf或者Wayback机器访问它:http://web.archive.org/web/20131117030438/http://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf。 - Eponymous
3个回答

55
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));

在Java中,Integer继承自Numberjava.lang.Number),因此直觉上,任何是Integer的东西也是一个数字,但是那篇文章指出,使用泛型时并不是这样的,因为考虑到例子,你可能会把一个浮点数(它属于Number类型)放进List<Integer>中,这是非法的,因为浮点数不是整数。

结论:泛型不支持协变。

注意:我建议您阅读Effective Java (2nd Edition) Chapter 5: Generics


谢谢。8)我以为解释与代码片段无关。我真傻。 - soocracy42
+1 推荐阅读《Effective Java》。我已经写代码多年了,一个月前开始阅读这本书,立刻学到了5或6个非常重要的策略。 - Kevin Day
这篇文章也非常有用:https://www.ibm.com/developerworks/java/library/j-jtp01255/index.html - Markus Jevring
1
有点离题,但是C#确实允许集合以安全的方式具有协变类型。请查看http://mikehadlow.blogspot.co.uk/2009/10/collection-covariance-with-c-40.html。 - simbo1905
所以,如果我有一个返回类型为List<Number>的方法,原则上我可以安全地返回一个List<Integer>,因为整数列表之后不会被引用了?(并不是说Java支持这种操作而没有警告) - Thomas Ahle

11

如果你能像这样做:

List<Float> foo;
List<Object> bar;

foo = new ArrayList<Float>();
bar = foo;

foo.add(1.0f);
bar.add("Hello");

如果可以这样做,事情会非常糟糕。在这个例子中,bar 是 foo 的别名,如果你这样做了,就会失去泛型存在的主要原因,那就是类型安全。


非常感谢。我不确定别名部分。这证实了我的假设是正确的。 - soocracy42
啊!现在“Hello”不仅在bar中(这可能还好),而且也在foo中。终于有意义了 :) 我想我可以声明一个列表转换方法,它必须复制列表,嗯。 - Thomas Ahle

-2
public class vechicle {
void drive(){
}
}
class car extends vechicle{
        //Covariance
    vechicle getObject(){
        return new car();
    }
        //contravariance
    car getmyObject(){
        return (car) new vechicle(); 
    }
}

return (car) new vechicle(); 会抛出 java.lang.ClassCastException 异常。 - Abhishek Nayak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接