据我所知,java编译器对泛型类执行类型擦除,以便类型参数的类型信息被Object或其上限替换。序列化和之后的反序列化如何恢复原来的类型?
例如,如果您有一个像这样的通用类:
public class TestGeneric<T extends Serializable> implements Serializable {
T value;
public TestGeneric(T value) {
this.value = value;
}
public T getValue() {
return this.value;
}
}
我希望 JVM 在运行时看到这个:
public class TestGeneric implements Serializable {
Serializable value;
public TestGeneric(Serializable value) {
this.value = value;
}
public Serializable getValue() {
return this.value;
}
}
但是如果我执行以下代码,我会得到一个类转换异常:
public static void main(String argv[]) {
File newFile = new File("./test.txt");
try {
if (!newFile.exists()) {
TestGeneric<Double> testGeneric = new TestGeneric<>(0.2d);
try (FileOutputStream fos = new FileOutputStream(newFile); ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(testGeneric);
}
} else {
try (FileInputStream fis = new FileInputStream(newFile); ObjectInputStream ois = new ObjectInputStream(fis)) {
try {
TestGeneric<Boolean> genericRead = (TestGeneric<Boolean>) ois.readObject();
Boolean isNotABoolean = genericRead.getValue();
System.out.println(isNotABoolean);
} catch (ClassNotFoundException e) {
System.err.println("Class not found!");
}
}
}
} catch(IOException e) {
System.err.println("IO Exception occurred, reason: " + e.getMessage());
}
}
对象反序列化后(第二次运行时)出现以下错误:
Exception in thread "main" java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.Boolean (java.lang.Double and java.lang.Boolean are in module java.base of loader 'bootstrap')
看起来即使在执行类型擦除之后,JVM 在运行时也知道该字段的确切类型。这怎么行?
class Abc<T extends Number> { T value; ... }
编译时,变成
class Abc { Number value; ... }
。这个类的用法如下:
Abc<Integer> abcInst = new Abc<>();
abcInst.value = 123;
int a = abcInst.value;
变成:
Abc abcInst = new Abc();
abcInst.value = Integer.valueOf(123); // visualized boxing
int a = ((Integer) abcInst.value).intValue(); // visualized boxing
当 abcInst 中的
value
不是 Integer 时,此转换失败,因此将生成您得到的异常(类型略有不同)。
您不需要使用序列化来获取此错误消息,滥用泛型并将一种泛型类型转换为另一种泛型类型,然后访问具有错误泛型类型的泛型属性也会导致此错误。