关于Java集合类反序列化泛型问题碰到的BUG
BUG再现
public class Test {
public static void main(String[] args) {
Set<Long> set = new HashSet<>();
long l1 = 1;
long l2 = 2;
set.add(l1);
set.add(l2);
String jsonInfo = JSON.toJSONString(set);
// 对序列化后的字符串反序列化然后操作数据
Set set1 = JSON.parseObject(jsonInfo, Set.class);
set1.remove(l1);
set1.forEach(System.out::println);
}
}
以上简单的示例代码,结果却和预想的不一样!
1
2
进程已结束,退出代码为 0
为什么会这样呢,为什么反序列化后无法remove掉l1?
关于Java集合的泛型
Java中的泛型通过类型擦除的方式来实现,通俗点理解,就是通过语法糖的形式,在java->.class转换的阶段,将List<String>擦除调转为List的手段。换句话说,Java的泛型只在编译期,Jvm是不会感知到泛型的。
集合的特性
我们都知道Java的集合中是无法存储基本数据类型的,所以Java引入了包装类,并且添加了自动装箱和拆箱机制,使得我们有些时候并不能感知到我们操作的其实不是基本数据类型。
问题解决
所以,答案就显而易见了,通过DeBug可以看到,反序列化后的集合类自动将数字变成了Byte类型,这时候想要删除Long类型的数字,不管是通过hash码还是equals都无法正确匹配。
那么,我们该怎么解决集合类反序列化的泛型问题?
答案就是,使用 TypeReference 来保留泛型信息。
public class Test {
public static void main(String[] args) {
Set<Long> set = new HashSet<>();
long l1 = 1;
long l2 = 2;
set.add(l1);
set.add(l2);
String jsonInfo = JSON.toJSONString(set);
// 对序列化后的字符串反序列化然后操作数据
Set<Long> set1 = JSON.parseObject(jsonInfo, new TypeReference<Set<Long>>() {});
set1.remove(l1);
set1.forEach(System.out::println);
}
}
或者也可以写成:
public class DaoTest {
public static void main(String[] args) {
Set<Long> set = new HashSet<>();
long l1 = 1;
long l2 = 2;
set.add(l1);
set.add(l2);
String jsonInfo = JSON.toJSONString(set);
// 对序列化后的字符串反序列化然后操作数据
TypeReference<Set<Long>> typeReference = new TypeReference<Set<Long>>() {};
Set<Long> set1 = JSON.parseObject(jsonInfo, typeReference.getType());
set1.remove(l1);
set1.forEach(System.out::println);
}
}
这时候输出的结果就是
2
进程已结束,退出代码为 0