同Class对象抛出java.lang.ClassCastException的原因及解决方法
问题描述
在对同一类的对象进行类型转换时遇到java.lang.ClassCastException错误,触发错误的代码如下:
try (Session session = configDatabaseService.getSessionFactory().openSession()) { List list = session.createQuery("FROM SparelabsPollStatusDetailEntity detail WHERE detail.dateAdded>:dateAdded") .setParameter("dateAdded", Instant.ofEpochMilli(new Date(System.currentTimeMillis() - (2 * 24 * 3600 * 1000L)).getTime())) .list(); for (Object spareLabsPollStatusDetail : list) { Optional<DimResident> dimResident = this.dimResidentRepository.findById( Math.toIntExact(((SparelabsPollStatusDetailEntity) (spareLabsPollStatusDetail)).getDimResidentId()) ); if (dimResident.isPresent()) { // 省略业务代码 } else { System.out.println("NOT Updated dimResident SpareLabsKey = [" + dimResident.get().getResidentID() + "]"); } } } catch (Exception e) { System.err.print(e); if (Logger.isErrorEnabled()) Logger.error(e); }
对应的错误信息:
java.lang.ClassCastException: class com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity cannot be cast to class com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity (com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity is in unnamed module of loader 'app'; com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @86fbeb3) 2022-09-25 18:31:56,588 WARN [HikariPool-1 housekeeper] com.zaxxer.hikari.pool.HikariPool$HouseKeeper: HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=55s727ms524µs300ns). 2022-09-25 18:31:57:310 [ERROR] [restartedMain:ID=22]:[SpareLabsClientServiceImpl.java:834] java.lang.ClassCastException: class com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity cannot be cast to class com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity (com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity is in unnamed module of loader 'app'; com.rossmoor.sparelabs.model.entities.SparelabsPollStatusDetailEntity is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @86fbeb3) at com.rossmoor.sparelabs.services.web.SpareLabsClientServiceImpl.updateSpareLabsKeysInDimResidentTable(SpareLabsClientServiceImpl.java:794) at com.rossmoor.sparelabs.services.web.SpareLabsClientServiceImpl.doDataSynchronization(SpareLabsClientServiceImpl.java:574) at com.rossmoor.sparelabs.SpareLabsApplication.run(SpareLabsApplication.java:147) at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:771) at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:755) at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) at com.rossmoor.sparelabs.SpareLabsApplication.main(SpareLabsApplication.java:47) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
错误含义
这个看似矛盾的异常,本质原因是类加载器不一致:
- 一个
SparelabsPollStatusDetailEntity类由系统类加载器(loader 'app')加载 - 另一个同名类由Spring Boot DevTools的
RestartClassLoader加载
Java中判断两个类是否为同一类型,不仅要看全类名,还要看加载它们的类加载器是否相同。即使字节码完全一致,不同类加载器加载的类也会被视为不同类型,因此无法互相转换。
解决方案
方案1:禁用Spring Boot DevTools(快速解决)
如果不需要热重启功能,直接移除项目依赖中的DevTools:
- Maven:删除pom.xml中这段配置
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
删除后重新构建项目即可。
方案2:配置DevTools,指定类加载规则(保留热重启)
如果需要保留热重启功能,可通过配置让实体类由系统类加载器加载,避免重复加载:
在src/main/resources/application.properties中添加:
# 指定实体类包不参与DevTools的重启类加载 spring.devtools.restart.exclude=com/rossmoor/sparelabs/model/entities/**
方案3:优化代码,使用泛型查询(辅助优化)
原代码使用未指定泛型的List,强制类型转换容易引发问题。改为泛型查询可在编译期发现类型问题,避免不必要的强制转换:
// 使用泛型Query直接获取指定类型的List List<SparelabsPollStatusDetailEntity> list = session.createQuery( "FROM SparelabsPollStatusDetailEntity detail WHERE detail.dateAdded>:dateAdded", SparelabsPollStatusDetailEntity.class ).setParameter("dateAdded", Instant.ofEpochMilli(new Date(System.currentTimeMillis() - (2 * 24 * 3600 * 1000L)).getTime())) .list(); // 循环时无需强制转换 for (SparelabsPollStatusDetailEntity spareLabsPollStatusDetail : list) { Optional<DimResident> dimResident = this.dimResidentRepository.findById( Math.toIntExact(spareLabsPollStatusDetail.getDimResidentId()) ); // 后续逻辑不变 }
内容的提问来源于stack exchange,提问作者Ram
相关产品推荐
相关产品推荐

