问题引入

ClassCastException异常很常见,就是类型转换异常,相信大家都很对不陌生;无论初Java的新手,亦或是职场老手皆有可能遇到这个异常。但是大多时候我们遇到这个异常都是我们主动(强制类型转换)做数据类型转换的时候报出来的。通过转换,可以指示Java编译器将给定类型的变量作为另一种变量来处理。对基本类型和引用类型都可以转换。Java语言规范定义了允许的转换,其中大多数可在编译时进行验证。

不过,某些转换还需要运行时验证。如果在此运行时验证过程中检测到不兼容,JVM就会引发ClassCastException异常。

基本数据类型转换,即int,double,float这些基本的数据类型,当高类型(高字节)转向低类型(短字节)时,如double转int,即需要强制转换。

对象引用类型转换,指的是子类强制转换为父类的转换。

问题描述

常见的情况即为:不熟悉的类之间关系而错误的将非子类的对象强制转换为父类的报错,LZ遇到的则是比较隐晦的类型转换,还折腾了一小会儿。因为dao层有个分页查询方法(老前辈留下的),因为是抽取的公用方法, 所以接口返回参数使用了泛型List<T>,同时对应xml的 resultMap 的 type 属性指定为 java.util.Map,我想目的就是为了返回不同类型可以直接接收,而不用再次做转换;

但是坑就在这里, 假如是直接查询返回给前端则貌似是没问题的, 但是如果是在service层做了例如遍历给一写额外属性赋值的操作那就嗝屁了, 即会报标题的类型转换异常:java.lang.ClassCastException: java.util.HashMap cannot be cast to xxx.entity.Xxx

问题原因

本质上是接口返回的List集合类型与我们定义接收大List集合类型不一致导致的类型转换异常。接口方法接收类型确是具体的对象类型集合 List<User> users,接口返回的是List<HashMap>;因为是接口指定的是泛型,所以这样直接接收是编译通过的。然后想都没想的直接for循环就会因为实际引用的对象集合为List<HashMap>,而你想操作的对象集合为List<User>, 肯定就会有类型转换异常, 因为他两非亲非故。以下我贴出代码

<resultMap id="BaseResultMap" type="java.util.Map">
		<id column="id" property="id" />
		<result column="name" property="name" />
	</resultMap>
<T> List<T> page(Map<String, Object> map);
List<User> users = userDao.page(map);
// 执行这行代码会报ClassCastException
Set<String> ids = users .stream().map(User::getId).collect(Collectors.toSet());

解决方案

最好的解决方案就是尽量不要再mybatis的xml配置文件中这样配置:<resultMap id="BaseResultMap" type="java.util.Map">,而是将type指定为具体的实体类;但是要想玩LZ上面那种方式的话,也是有解决方案的,就是需要先转换一番。 将查询到的 users 先通过 JSON序列化为json字符串, 然后再将json字符串反序列化为执行的对象集合,代码实例如下:

String jsonStr = JacksonUtils.write2JsonString(users);
users = JacksonUtils.readJson2EntityByTypeReference(jsonStr, new TypeReference<List<User>>() {});
Set<String> codes = users.stream().map(Dealer::getId).collect(Collectors.toSet());