我从 Thinking in Java 这本书中获得了这段代码,其中Bruce表示调用方法 set()的警告。代码如下:
package testPackage;
class GenericBase<T> {
private T element;
public void set(T arg) { arg = element; }
public T get() { return element; }
}
class Derived1<T> extends GenericBase<T> {}
class Derived2 extends GenericBase {} // No warning
// class Derived3 extends GenericBase<?> {}
// Strange error:
// unexpected type found : ?
// required: class or interface without bounds
public class ErasureAndInheritance {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Derived2 d2 = new Derived2();
Object obj = d2.get();
d2.set(obj); // Warning here!
}
}
如果删除注释,我会收到以下警告:
类型安全:方法集(Object)属于原始类型GenericBase。对泛型类型GenericBase的引用应该参数化
我的问题是,为什么警告会显示在 set()方法上?有人可以解释这个警告的含义吗? 顺便说一句,我是Java Generics的新手,虽然我读了关于泛型的其他问题,但我仍然对 Erasure 感到困惑。
答案 0 :(得分:3)
来自Java doc here
泛型被引入Java语言以提供更紧密的类型 在编译时检查并支持通用编程。至 实现泛型,Java编译器将类型擦除应用于:
- 如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。生成的字节码, 因此,只包含普通的类,接口和方法。
- 如有必要,插入类型转换以保护类型安全。
- 生成桥接方法以保留扩展泛型类型中的多态性。
- 类型擦除确保不为参数化类型创建新类;因此,泛型不会产生运行时开销。
简单:在编译过程之后,每个泛型都是Object
,它只是为你添加了转换,如果你做错了,它将产生编译错误。
关于unchecked
注释,当编译器无法确定您正在做什么是正确的时(例如,您使用的原始类型与GenericBase<Object>
)
除了set
类型之外,您有T
方法发出的警告,但由于您使用的是原始类型,因此您不知道T
是什么,并生成此警告让你知道你正在使用原始类型(坏事)。
您可以使用注释说:&#34;我知道它是原始类型,但我知道它是合法的,我知道我在做什么&#34;。
混合原始类型和泛型类型是可能的,因为泛型的实现方式,如上所述,在编译器处理后T
变为Object
,这意味着像{一样的原始对象{1}}与GenericBase
相同。
它没有要求使用泛型进行任何转换的原因是因为它将为您添加转换(并且您可以确保代码将始终在运行时工作而不用担心可能{{1 }})。