面试题答案
一键面试泛型的类型擦除机制
在Java中,泛型主要是在编译期提供类型安全检查,编译之后,所有的泛型信息都会被擦除,替换为对应的原始类型。例如,List<String>
在编译后实际类型是List
,String
类型信息被擦除。
多态向下转型遇到的特殊情况
- 类型擦除导致运行时无法区分具体类型:由于类型擦除,在运行时JVM无法获取泛型的实际类型参数。例如,有两个类
SubTypeA<T>
和SubTypeB<T>
继承自SuperType<T>
,当进行向下转型时,如SuperType<String> superObj = new SubTypeA<>(); SubTypeA<String> subObj = (SubTypeA<String>) superObj;
,编译时因为泛型擦除,实际检查的是SuperType
和SubTypeA
的继承关系,运行时无法确认泛型参数String
,如果转型逻辑依赖于泛型参数类型,就会出现问题。 - ClassCastException风险:在泛型类型擦除后,当不恰当的向下转型时,容易抛出
ClassCastException
。比如,有List<String> strList = new ArrayList<>(); List<Integer> intList = (List<Integer>) strList;
,编译期由于泛型擦除认为是合法的List
类型转换,但运行时实际类型不匹配就会抛异常。
妥善处理方式
- 使用instanceof检查:在向下转型前,使用
instanceof
进行类型检查。例如:
SuperType<?> superObj = new SubTypeA<>();
if (superObj instanceof SubTypeA) {
SubTypeA<?> subObj = (SubTypeA<?>) superObj;
// 进行后续操作
}
- 增加类型标识字段:在泛型类中添加一个字段来标识实际类型。例如:
class SuperType<T> {
private Class<T> type;
public SuperType(Class<T> type) {
this.type = type;
}
public Class<T> getType() {
return type;
}
}
class SubTypeA<T> extends SuperType<T> {
public SubTypeA(Class<T> type) {
super(type);
}
}
// 使用时
SuperType<String> superObj = new SubTypeA<>(String.class);
if (superObj.getType() == String.class && superObj instanceof SubTypeA) {
SubTypeA<String> subObj = (SubTypeA<String>) superObj;
// 处理逻辑
}
- 借助反射:通过反射获取实际类型信息来辅助转型。例如:
SuperType<?> superObj = new SubTypeA<>();
if (superObj.getClass() == SubTypeA.class) {
ParameterizedType genericSuperclass = (ParameterizedType) superObj.getClass().getGenericSuperclass();
Type typeArg = genericSuperclass.getActualTypeArguments()[0];
// 根据typeArg进行进一步的转型逻辑判断和处理
SubTypeA<?> subObj = (SubTypeA<?>) superObj;
}