首页 > 其他分享 >集合泛型不匹配导致的ClassCastException异常解决

集合泛型不匹配导致的ClassCastException异常解决

时间:2022-12-23 18:02:15浏览次数:34  
标签:匹配 代码 List 类型 ClassCastException 擦除 泛型 集合


一. 代码重现

前几天壹哥的一个学生小K编写集合代码时,运行的结果中却出现了一个自己没见过的异常,他不知道怎么解决,于是就跑来找壹哥帮忙。下面就是小K的代码,大家可以来看看,如下所示:

集合泛型不匹配导致的ClassCastException异常解决_linq

上述代码,一旦运行就会出现下图中的异常现象。说实话,对壹哥来说,只要一遇到bug,真是瞬间感觉连吃大盘鸡都不香了,必须立马盘它才行。

集合泛型不匹配导致的ClassCastException异常解决_List_02

上图中,我们看到了一个叫做ClassCastException类型转换的异常!为什么会出现这个bug呢?其实如果我们仔细检查一下代码,就会发现原来是集合中的值写错了!我们声明的集合泛型是Double类型的,结果添加数据元素的时候,给集合添加了一个整型的元素,这样就造成了上述异常。而且根据错误信息的提示,异常出现在代码的第40行位置,现在我们知道了异常的原因和位置,接下来解决就容易了。

二. bug分析

其实上述代码中之所以会出现问题,是因为集合对泛型的严格要求所导致的。一开始小K觉着int类型可以直接转换为double类型,所以就往集合中添加了整型数据。但实际上Java中的集合泛型,要求的必须是包装类,我们的代码中就是Interger和Double,所以是无法将基本类型直接转为包装类型的。但小K却不明白,为什么基本类型与包装类型两者的类型不一致,但在往集合中添加数据时却可以添加进去呢?为了给小K讲明白这个问题,壹哥就通过javap命令带小K查看了反编译后的List类型,我们来看看泛型的底层究竟是个什么情况,如下图所示:

集合泛型不匹配导致的ClassCastException异常解决_java_03

通过反编译可以看出,集合在底层编译时,其实所谓的泛型都被擦除了。也就是说,当我们在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。所以List<Interger>和List<Double>在运行时事实上是相同的类型。而这其中原始类型的集合是特别容易出问题的,因为原始类型会跳过泛型检查且很不安全,List、List<?> 和 List<Object> 等存在着巨大的差异,泛型在使用中很容易造成类型擦除。那么到底什么是泛型擦除?我们继续往下看。

三. 泛型的擦除

我们知道,Java泛型这个特性并不是一开始就有的,而是从JDK 1.5才开始加入的。因此Sun公司为了兼容之前的旧版本,Java对泛型的实现采取了“伪泛型”的策略,也就是说Java在语法上支持泛型,但在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。

并且泛型在擦除的时候,还会根据泛型的具体类型来进行。

四. 泛型相关知识回顾

泛型其实就是引用类型的占位符,主要用于避免引用类型的相互转换,替换之前使用的Object类型,去除不同类型之间的强转。

1. 泛型的应用

泛型主要可以用在 泛型类、泛型接口、泛型方法 上,也就是说,泛型可以在类、接口、方法上使用。

2. 泛型通配符

在泛型中,有几个常用的通配符,我们需要掌握。

?:表示右边的泛型可以是任意类型,还可以指定一个泛型的上限和下限。

泛型上限:

- 语法格式: 类型名称 <? extends 类 > 对象名称

- 语法意义: 只能接收该类型及其子类

泛型下限:

- 语法格式: 类型名称 <? super 类 > 对象名称

- 语法意义: 只能接收该类型及其父类型

3. 具体用法,如下图所示:

集合泛型不匹配导致的ClassCastException异常解决_c#_04

 现在你知道为什么会出现上述异常了吗?对泛型的理解是否有进一步加深了呢?如果还有不明白的地方,可以在评论区给壹哥留言,我看到了会及时给你回复的。

标签:匹配,代码,List,类型,ClassCastException,擦除,泛型,集合
From: https://blog.51cto.com/u_7044146/5966097

相关文章