近期在线上环境遇到一个奇怪的问题。关于类型推断、泛型、重载的问题。
问题发现
一段原本运行良好的代码,在升级为JDK8后,突然遇到了异常情况。
java.lang.ClassCastException: java.lang.String cannot be cast to [C
大家面面相觑、非常困惑。明明没有任何代码变更
- 怎么就不行了呢?
- 怎么就会报类型转换异常呢?
[C类型是个啥?
[c是char[[ 数组类型。截图中显示 String类型转为char[] 类型异常。
一看到异常堆栈就感到困惑,再仔细看代码,更觉得不可思议~
抛出异常的代码处既有嵌套调用,又涉及业务逻辑。为了保护代码机密性,我对相关部分进行了转换。
@Test
public void test2() {
System.out.println(String.valueOf(getSimpleString()));
}
public static <T> T getSimpleString() {
return (T) "121";
}
调用堆栈并没有明确异常抛出的方法,只是在 String.valueOf(getSimpleString()) 这一行抛出了异常。这行代码怎么看也不会有问题吧?
问题解决
正当一筹莫展之际,有人怀疑是升级JDK8的问题。尝试在本地环境使用JDK8复现问题。最终确认JDK8环境下稳定复现这个问题。
然后尝试分割这两个方法,再次验证。
String value = getSimpleString();
System.out.println(String.valueOf(value));
将泛型返回值声明为 String以后,问题解决,不会再有类型转换异常。
虽然解决了问题,但是非常困惑,为什么String.valueOf 将一个String泛型对象,转为String,会报类型转换异常呢?
离谱、很离谱。为什么会出现这个问题,为什么换成两行代码,问题解决了?

正常情况下应该使用 String.valueOf(Object value)这个重载方法,为什么会选择valueOf(char[])呢?JDK8 为什么会选择一个错误的重载方法
总结
在查阅无数资料后,看起来比较可信的解释是
如果在编译阶段不能够确认 返回值类型,在运行时,jdk8会对泛型返回值类型进行推断,如果恰好方法存在重载,jdk就逐个对方法进行类型比对,如果类型可以匹配上,则使用相关重载方法,如果无法匹配,则使用 valueOf(char[])。
我做了类似的测试,返回值类型为
- (T)"123"
- (T)
new Long(1212L) - (T)
new Integer(1212L) - (T) new Order();
(T) new Object()
无论是String类型,还是Long等包装器类型、Object类型、业务自定义类。都报出类型转换异常。
当泛型返回值为Object对象时,居然也抛出异常,从现象来看,jdk8并没有逐个比较重载方法是否类型一致,而是选择了char[] 这个默认方法。
实际上任何类型只要没有显示声明返回值类型,使用泛型返回值且编译时不确定类型,使用String.valueOf方法,那么就会出现类型转换异常。
对于泛型返回值不明确的的情况,JDK6、7中,会自动视为Object类型。为什么jdk8 这么干,咱也不确定是咋想的,姑且认为是bug吧。
所以也建议大家,针对泛型返回值,一定要使用字段明确声明具体的类型,避免jvm在运行时错误的推断返回值类型。在和String.valueOf等重载方法一起使用时,要格外小心。
我的开源项目
最后夹带一点私货,五阳最近花了3个月的时间完成一个开源项目。
开源3周以来,已有近 230 多个关注和Fork
Gitee:gitee.com/juejinwuyan…
GitHub github.com/juejin-wuya…
开源平台上有很多在线商城系统,功能很全,很完善,关注者众多,然而实际业务场景非常复杂和多样化,开源的在线商城系统很难完全匹配实际业务,广泛的痛点是
- 功能堆砌,大部分功能用不上,需要大量裁剪;
- 逻辑差异点较多,需要大量修改;
- 功能之间耦合,难以独立替换某个功能。
由于技术中间件功能诉求较为一致,使用者无需过多定制化,技术中间件开源项目以上的痛点不明显,然而电商交易等业务系统虽然通用性较多,但各行业各产品的业务差异化极大,所以导致以上痛点比较明显
所以我在思考,有没有一个开源系统,能提供电商交易的基础能力,能让开发者搭积木的方式,快速搭建一个完全契合自己业务的新系统呢?
- 他们可以通过编排和配置选择自己需要的功能,而无需在一个现成的开源系统上进行裁剪
- 他们可以轻松的新增扩展业务的差异化逻辑,不需要阅读然后修改原有的系统代码!
- 他们可以轻松的替换掉他们认为垃圾的、多余的系统组件,而不需要考虑其他功能是否会收到影响
开发者们,可以择需选择需要的能力组件,组件中差异化的部分有插件扩展点能轻松扩展。或者能支持开发者快速的重新写一个完全适合自己的新组件然后编排注册到系统中?
memberclub 就是基于这样的想法而设计的。 它的定位是电商类交易系统工具箱, 以SDK方式对外提供通用的交易能力,能让开发者像搭积木方式,从0到1,快速构建一个新的电商交易系统!
具体介绍可参见
Gitee开源地址:gitee.com/juejinwuyan…
GitHub开源地址 : github.com/juejin-wuya…
在这个项目中你可以学习到 SpringBoot 集成 以下框架或组件。
- Mybatis、Mybatis-plus 集成多数据源
- Sharding-jdbc 多数据源分库分表
- redis/redisson 缓存
- Apollo 分布式配置中心
- Spring Cloud 微服务全家桶
- RabbitMq 消息队列
- H2 内存数据库
- Swagger + Lombok + MapStruct
同时你也可以学习到以下组件的实现原理
- 流程引擎的实现原理
- 扩展点引擎实现原理
- 分布式重试组件实现原理
- 通用日志组件实现原理 参考:juejin.cn/post/740727…
- 商品库存实现原理: 参考:juejin.cn/post/731377…
- 分布式锁组件: 参考:
- Redis Lua的使用
- Spring 上下文工具类 参考: juejin.cn/post/746927…