今天分享一个写代码时遇到的诡异问题,如何排查解决的。
从事互联网的人都懂,一般遇到问题时,首先会想用谷歌、百度等搜索引擎,看看前辈们是如何解决的。
但有些问题比较抽象,不知道如何来描述,怎么办?或者勉强描述清楚了,搜出来的答案也很难满足要求,整个过程犹如大海捞针。最后求助 ChatGPT 瞬间解决,给大家分享下解决思路。
正在开发一个项目,仓储层有一个接口类(IProductReadRepository),其中一个方法的入参使用了 Java 泛型,具体如下:
List<SpuVO> batchQuerySpuBySpuIdsFromDB(ProductQueryWrapper<List<Long>> req);
在上层的领域服务中,有一处调用这个方法的代码,具体如下:
ProductQueryWrapper productQueryWrapper = ProductQueryWrapper.builder()
.bizCode(BusinessCodeEnum.SMART.getCode())
.bizScene(BusinessSceneEnum.RETAIL.getCode())
.storeId(multiPackageVO.getStoreId())
.bizParams(spuIds)
.build();
List<ProductSummaryVO> productSummaryVOS = productReadRepository.batchQuerySpuBySpuIdsFromDB(productQueryWrapper);
if (CollectionUtils.isEmpty(productSummaryVOS)) {
bizResult.error(BizErrorCode.MULTI_PACKAGE_PRODUCT_NOT_EXIST);
return bizResult;
}
其中,List<ProductSummaryVO> productSummaryVOS = productReadRepository.batchQuerySpuBySpuIdsFromDB(productQueryWrapper);?这行代码非常诡异
返回的结果List<ProductSummaryVO>并不匹配,但在IDEA中却没有报错,这让我们感到非常困扰。
这是怎么回事?
当时怀疑是错觉,仔细对比了两个方法,确实是对的。
又怀疑是 IDEA 的缓存干扰导致,所以将工程重新 build 了一次还是不行!
接下来,能想到的各种招数都招呼上,比如:
最后,使出绝招,将本地的工程代码全部删除,然后重新从 git 上拉取最新代码,编译,导入到 IDEA 工程。
但,但,但,问题依然存在,真让人崩溃。
难道是 IDEA 中的错误检查失效了,于是人为制造了一些错误,发现 IDEA 也都能识别出来。
实在没招了。。。。。。。
最后,还是问下 ChatGPT 吧
首先,先把问题描述清楚,ChatGPT 特懂人情世故,你跟他讲清楚问题,他也会给你一个清楚的答案。
有一个接口内的方法定义如下:
List<SpuVO> batchQuerySpuBySpuIdsFromDB(ProductQueryWrapper<List<Long>> req);
使用下面的代码来调用 batchQuerySpuBySpuIdsFromDB,返回的 List<SpuVO> 结果并不匹配,但在 IDEA 中没有报错,是什么原因?
ProductQueryWrapper productQueryWrapper = ProductQueryWrapper.builder()
.bizCode(BusinessCodeEnum.SMART.getCode())
.bizScene(BusinessSceneEnum.RETAIL.getCode())
.storeId(multiPackageVO.getStoreId())
.bizParams(spuIds)
.build();
List<ProductSummaryVO> productSummaryVOS = productReadRepository.batchQuerySpuBySpuIdsFromDB(productQueryWrapper);
if (CollectionUtils.isEmpty(productSummaryVOS)) {
bizResult.error(BizErrorCode.MULTI_PACKAGE_PRODUCT_NOT_EXIST);
return bizResult;
}
ChatGPT 很快就给出了答案。
问题可能出在泛型擦除和编译时类型检查上。由于Java泛型的擦除机制,在运行时无法获取泛型的实际类型,这可能导致类型不匹配的问题。
那我们就按照它的思路来,将这行代码 ?ProductQueryWrapper productQueryWrapper = ProductQueryWrapper.builder()
修改为 ?ProductQueryWrapper<List<Long>> productQueryWrapper = ProductQueryWrapper.<List<Long>>builder()
在构建 ProductQueryWrapper 对象时, 指定泛型参数的具体类型,以便在运行时能够正确地识别参数类型。
然后,问题真的解决了。
如下图所示,第二处红框位置的代码开始报错。因为接口的规范(返回结果)调整了,此处确实应该报错提示。
后面,我们根据错误提示,将?ProductSummaryVO?类替换成?SpuVO?类。
最终,不但编译不报错,单元测试也能跑通。问题完美解决。
为何在 IDEA 中没有直接报错呢?这里牵扯到Java泛型的类型推断机制。
上述代码中,虽然使用了原始类型?ProductQueryWrapper?,但没有指定具体的泛型类型。
Java 7 及以后的版本引入了菱形操作符(Diamond Operator),允许在创建对象时不再重复指定泛型类型,而是通过上下文进行类型推断。
这就意味着在你的代码中,虽然没有明确指定泛型类型,但由于在 ProductQueryWrapper.builder() 上下文中,编译器会尝试根据调用方的期望类型来推断泛型参数。
这种类型推断机制使得在?IDEA?开发工具不会直接报错,也就出现了上文说到的那个问题。