- 字节码层面的继承关系:
- 在Java的类型擦除机制下,泛型类型参数
T
在编译后会被擦除。对于GenericClass<T>
,擦除后变为GenericClass
,对于SubGenericClass extends GenericClass<String>
,擦除后变为SubGenericClass extends GenericClass
。所以在字节码层面,SubGenericClass
继承自擦除类型后的GenericClass
。
- 类型擦除对继承关系的影响:
- 编译时的强类型检查丢失:编译时依靠泛型进行的强类型检查,在运行时由于类型擦除而不存在。例如,在编译时
GenericClass<String>
只能操作String
类型的数据,但运行时擦除后,可以向其添加其他类型的数据(当然,这会导致运行时错误)。
- 桥接方法的产生:为了保证多态性,在泛型子类中会产生桥接方法。比如当子类重写父类基于泛型的方法时,由于类型擦除,为了保持多态行为的正确性,编译器会生成桥接方法。
- 举例说明可能出现的问题:
import java.util.ArrayList;
import java.util.List;
class GenericClass<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
class SubGenericClass extends GenericClass<String> {
@Override
public void setValue(String value) {
super.setValue(value);
}
}
public class Main {
public static void main(String[] args) {
SubGenericClass sub = new SubGenericClass();
GenericClass gc = sub;
// 编译时不会报错,但运行时会抛出ClassCastException
gc.setValue(10);
String result = sub.getValue();
}
}
- 在上述代码中,由于类型擦除,
gc.setValue(10)
编译时不会报错,因为擦除后GenericClass
的setValue
方法参数变为Object
类型。但在运行时,sub.getValue()
期望返回String
类型,而实际返回的是Integer
,就会抛出ClassCastException
。
- 桥接方法相关问题(以重写泛型方法为例):
class GenericBase<T> {
public T get() {
return null;
}
}
class GenericSub extends GenericBase<String> {
@Override
public String get() {
return "Hello";
}
}
- 在字节码层面,编译器会为
GenericSub
类生成一个桥接方法public Object get()
,这个桥接方法内部调用public String get()
方法。如果在反射调用等场景下不小心使用了这个桥接方法,可能会导致与预期不符的行为。