面试题答案
一键面试问题分析
- 类型擦除导致静态泛型变量类型丢失:Java泛型的类型擦除机制在编译后会将泛型类型替换为其限定类型(通常是Object)。对于静态变量使用泛型,在运行时无法区分不同泛型参数实例化的类型,因为所有实例共享静态变量,而静态变量最终只有一份基于擦除类型的数据。
- 运行时类型检查问题:由于类型擦除,在运行时可能出现类型转换异常。例如,原本期望某种特定类型的数据存入静态泛型变量,但由于擦除后变成Object类型,可能存入不匹配的数据,在后续取出时导致
ClassCastException
。
示例代码及问题展示
public class StaticGenericProblem {
// 静态泛型变量
private static <T> T staticGenericVar;
public static <T> void setStaticGenericVar(T value) {
staticGenericVar = value;
}
public static <T> T getStaticGenericVar() {
return staticGenericVar;
}
public static void main(String[] args) {
// 第一次设置为String类型
setStaticGenericVar("Hello");
String result1 = getStaticGenericVar(); // 编译通过,运行时可能出现问题
// 第二次设置为Integer类型
setStaticGenericVar(123);
// 这里如果不进行强制类型转换,会因为类型擦除导致编译通过,但运行时可能出现ClassCastException
Integer result2 = (Integer) getStaticGenericVar();
}
}
在上述代码中,staticGenericVar
是一个静态泛型变量。在main
方法中,先将其设置为String
类型,又设置为Integer
类型。由于类型擦除,编译时不会报错,但运行时可能出现类型转换异常。
解决方法
- 使用通配符和边界来限制类型:可以通过设置类型边界来限制可以传入的类型,同时使用通配符
?
来处理不同类型的实例化。
public class StaticGenericSolution {
// 使用通配符和边界定义静态变量
private static <T extends Number> T staticGenericVar;
public static <T extends Number> void setStaticGenericVar(T value) {
staticGenericVar = value;
}
public static <T extends Number> T getStaticGenericVar() {
return staticGenericVar;
}
public static void main(String[] args) {
setStaticGenericVar(123);
Integer result = getStaticGenericVar();
setStaticGenericVar(12.34);
Double result2 = getStaticGenericVar();
}
}
在这个改进的代码中,staticGenericVar
被限制为Number
及其子类类型。这样可以确保存入的数据类型符合预期,减少运行时类型转换异常的风险。
2. 避免在静态上下文中使用泛型:可以将泛型逻辑移到实例方法中,每个实例可以有自己的泛型类型参数,避免共享静态变量带来的类型混淆问题。
public class AvoidStaticGeneric {
private <T> T instanceGenericVar;
public <T> void setInstanceGenericVar(T value) {
instanceGenericVar = value;
}
public <T> T getInstanceGenericVar() {
return instanceGenericVar;
}
public static void main(String[] args) {
AvoidStaticGeneric obj1 = new AvoidStaticGeneric();
obj1.setInstanceGenericVar("Hello");
String result1 = obj1.getInstanceGenericVar();
AvoidStaticGeneric obj2 = new AvoidStaticGeneric();
obj2.setInstanceGenericVar(123);
Integer result2 = obj2.getInstanceGenericVar();
}
}
在这个示例中,将泛型变量定义在实例层面,每个实例可以独立管理自己的泛型类型,避免了静态泛型变量共享带来的问题。