一、前言
最近在做应用迁移时候遇到了一个诡异的类型转换问题,感觉比较有意思,就记录下来和大家分享下。
原始系统里面有的一个从数据库查询的伪代码如下:
image
image
由于Convert.asInt是webx框架的方法,而现在多维群发是要迁移到springboot上,所以使用下面方法替换了该方法:
image.png
修改后日常环境部署测试抛出如下异常:
Exception in thread "main" java.lang.ClassCastException: java.math.BigInteger cannot be cast to java.lang.String
问题自此产生
下面做个小实验来证明会成功。
image.png
如上代码编译和运行期都是OK的,那么为何会成OK的那,(2)处类型转换时候不应该出错? 其中(1)(2)注释分别为IDE的提示语
HashMap<Object,Object>
. 而BigInteger是继承自Object的。Map<String,String>
不进行类型检查。其实Java中泛型与C++的模板是有区别的,C++中指定模板参数后编译后生成的类是使用真实类型替换后的,而Java中泛型编译后生成的类中的类型都是Object,这也叫类型擦除。下面举个例子证明:
image.png
结果会返回true。
也就是说HashMap和HashMap<String,String>
编译后生成的实例都是HashMap<Object,Object>
。
image.png
如上代码,运行后到底哪里会抛出异常? 其实是第82行代码,那么81行为何不抛出异常那,其实是因为实验2,因为运行时候HashMap<String,String>
其实是HashMap<Object,Object>
,调用get方法时候调用的就是HashMap的get方法,而82句执行时候会根据=左侧String类型在get的时候进行类型转换也就是进行BigInteger到String的转换,所以异常抛出。
image
image
List<Map<String, String>>
,修改其为List也可以解决,还有持久层一般最好不要返回Map类型,而返回一个DO,DO里面都是严格类型的,不容易出错。由于Java泛型是编译后生成的类都是原始类型的,所以虽然编译时候不出错,但是运行时还是有可能出现类型转换问题的,不指定泛型参数时候,默认是Object类型,要注意不要使用Object类型的泛型集合赋值到其他泛型类型。另外使用持久化框架时候返回值最后是封装好的Do对象,Do对象里面的属性都是严格类型的,不容易出现问题。