为什么超类泛型类型没有被擦除/强制转换



考虑以下代码:

class A<T>{
T t;
public T getValue(){
    return t;
}
class B<S extends Number> extends A<String>{
//some code here..
}
B b = new B<Integer>();
String name = b.getValue() // This throws compilation error

而以下工作:

B<Integer> b = new B<Integer>();
String name = b.getValue() // This works...!

所以我的问题是:

  1. 我必须给出所有涉及的泛型类型的声明吗?(尽管A类的泛型类型是在B类中声明的。)

  2. 或者,我基本上错过了什么?

参见JLS 4.8原始类型:

原始类型被定义为[…]引用类型,该引用类型是在没有附带类型参数列表的情况下采用泛型类型声明的名称形成的。

原始类型的超类(分别为超接口)是对其任何参数化调用的超类的擦除。

换句话说,当您在不使用<>的情况下使用B时,它会变成一个原始类型,基本上会擦除所有泛型,一直到所有基类和接口,也就是说,就好像AB不是泛型:

class A {
    Object t;
    public Object getValue() {
        return t;
    }
}
class B extends A {
    //some code here..
}

这就是String name = b.getValue()失败的原因,因为getValue()现在返回一个Object


请注意JLS中的以下注释:

只允许使用原始类型作为对遗留代码兼容性的让步强烈反对在Java编程语言中引入泛型后编写的代码中使用原始类型。Java编程语言的未来版本可能会禁止使用原始类型。

简而言之,不要。将代码修复为不使用原始类型。

但是,您可以按如下方式缩短右侧:

B<Integer> b = new B<>();

类B的泛型值与类A的泛型值没有链接。

a(Long)的方法T getValue()将是Long getValue((),忽略包装类上的任何泛型。

如果你想要一个Integer getValue(),那么只需选择

class B extends A<Integer> {}

编辑:你改变你的问题,让我们改变回答:)

B被认为是B(S扩展了Number,T)(因为它扩展了A)。如果将变量存储为原始类型B,则即使声明正确,它也是隐式的B(Object,Object)。

使用列表进行测试

List l = new ArrayList<String>();
String s = l.get(0);

你会以同样的问题结束。

最新更新