Java:在泛型类中使用类型化变量

4

请问有人能解释一下为什么这段代码无法编译吗?

即使它使用了一个没有提供具体类型T的泛型类,它应该能够在编译时识别出ArrayList保存的是字符串。

public class Test {
    public static void main(String[] args){
        Container container = new Container();
        container.strings.add("test");
        String s1 = container.strings.get(0); // does not compile
        ArrayList<String> local = container.strings;
        String s2 = local.get(0); // does compile
    }

    static class Container <T>{
        ArrayList<String> strings = new ArrayList<String>();
    }
}

5
编译错误难道对找出为什么无法编译有帮助吗? - K-ballo
2个回答

7
当你使用一个通用类作为原始类型时(即你没有指定类型的情况下),所有的通用信息都被从该类中删除(无论省略的类型是否被使用)。
因此,当你编写 Container container(而不是 Container<SomeClass> container)时,ArrayList<String> strings 变成了 ArrayList strings,就像它是 ArrayList<Object> 一样被使用。
要进行“修正”,请为 Container 指定类型(即使你不使用该类型)。
Container<Object> container = new Container<Object>();

其余部分现在将被编译。


这样做的原因是为了与早期的Java(1.4及更早版本)向后兼容。


那听起来有点极端...他们为什么决定这样做? - K-ballo
2
@K-ballo:因为Java泛型是事后粘贴的,而且在运行时一切都是“Object”。泛型只在编译时真正发挥作用。 - Matti Virkkunen
1
@Bohemian:我仍然不明白为什么需要剥离与泛型参数无关的具体类型,但是...想想也无所谓了:P - K-ballo
1
@Raffaele Google 给了我这些 Stack Overflow 链接:这个简短的这个权威的 - Bohemian
1
@Raffaele 我完全理解你的观点,可能也同意。 - Bohemian
显示剩余6条评论

0
正如Bohemian所说,原始类型中的每个类型参数都被丢弃了。起初我以为这是一个错误,但在错误数据库(#6244346)中甚至有一篇文章明确引用了相关的JLS §4.8
一个没有从其超类或超接口继承的原始类型C的构造函数(§8.8)、实例方法(§8.4、§9.4)或非静态字段(§8.3)M 的类型是对应于 C 的泛型声明中其类型被消除后的原始类型。 原始类型C的静态方法或静态字段的类型与对应于C的泛型声明中的类型相同。 将类型参数传递给未从其超类或超接口继承的原始类型的非静态类型成员是编译时错误。 尝试将参数化类型的类型成员用作原始类型是编译时错误。
您不能从原始List获取String,但可以将List赋给List的原因是,在后者中,编译器发出警告(未经检查的转换),但您不报告它(您阅读警告吗,不是吗?:P)。我使用javac和Eclipse编译器测试了您的代码,两者都遵循规范。

原始类型是为了与旧代码进行互操作而引入的,但在这种情况下,我无法想出一种保留非静态成员类型信息的方法,这可能会破坏某些东西。使用参数化类型而不是原始类型意味着代码部分移植,因此在这种情况下,目的不仅仅是向后兼容,而是确保一个连贯的代码库,无论代码是完全1.4还是完全Java 5+。另一个选择是,像这样使用原始类型可能会减缓在类似上下文中采用无界通配符的速度。

顺便说一句(但我认为你已经自己想到了),如果您不使用类型参数,可以简单地使用无界通配符,即Container<?>


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