中所周知,Java 中的泛型并不像 C++、C# 一样是真正的泛型,其泛型是通过类型擦除来实现的。具体什么是类型擦除,可以参看这篇博文:。今天要记录的是在实际开发中遇到的一个,由于 Java 这种泛型的实现方式而导致的问题,及解决办法。
一下代码是模拟真实开发环境下的实现:
- 1@Test 2 public void test() {
- 3 // 构建searchMap,模拟前端传来的查询参数
- 4 Map searchMap = new HashMap();
- 5 List goodsIds1 = new ArrayList();
- 6 goodsIds1.add(1);
- 7 goodsIds1.add(2);
- 8 goodsIds1.add(3);
- 9 searchMap.put("goodsIds", goodsIds1);
- 10 searchMap.put("goodsType", 1);
- 11 12 // 利用searchMap进行查询,模拟后端的逻辑
- 13 List goodsIds2 = (List) searchMap.get("goodsIds");
- 14
- for (Long goodsId: goodsIds2) {
- 15 System.out.println(goodsId);
- 16
- }
- 17
- }
这里的 searchMap 用来接收前端传来的查询商品信息的参数,假设要查询商品 id 分别为 1、2、3,同时商品类型为 1 的商品。后端逻辑会从 searchMap 中获取 goodsIds 的 list,然后循环查询每一个商品的信息。以上代码在 eclipse 中不会提示任何错误,但其实在运行的时候会抛出 java.lang.ClassCastException 异常。
问题就在于第 9 行
- searchMap.put("goodsIds", goodsIds1);
中 goodsIds1 是 List<Integer> 类型的,而第 13 行
- List < Long > goodsIds2 = (List < Long > ) searchMap.get("goodsIds");
在取出 goodsIds 的时候,虽然强制转换为 List<Long> 型,但实质上,goodsIds2 中的值为 Integer 型,如下图:
所以在第 14 行遍历 goodsIds2 的时候
- for (Long goodsId: goodsIds2) {
- System.out.println(goodsId);
- }
就会抛出 java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long 异常。这里本质就是由于 List<Integer> 和 List<Long>, 在编译之后,其泛型信息都被擦除,都被视为 List,所以取出时能够转换成功。
其实在第 13 行,IDE 会给出警告:Type safety: Unchecked cast from Object to List<Long>,虽然可以通过在方法上加注解:@SuppressWarnings("unchecked") 来消除警告,但这只是起到标示作用,并不会修正错误。最简单的修复的办法就是将 goodsIds2 也声明为 List<Integer>,然后再遍历的时候转为 Long 型,但是不太优雅。另外一种解决方法就是,用 Java 类去接收前端传来的参数,而不是用 Map,但是这样的话需要增加一个 POJO 类。那为什么不直接将 goodsIds1 也声明为 List<Long> 型呢?像这样:
- 1@Test 2 public void genericTest() {
- 3 // 构建searchMap,模拟前端传来的查询参数
- 4 Map searchMap = new HashMap();
- 5 List goodsIds1 = new ArrayList();
- 6 goodsIds1.add(1L);
- 7 goodsIds1.add(2L);
- 8 goodsIds1.add(3L);
- 9 searchMap.put("goodsIds", goodsIds1);
- 10 searchMap.put("goodsName", "商品1");
- 11 12 // 利用searchMap进行查询,模拟后端的逻辑
- 13 List goodsIds2 = (List) searchMap.get("goodsIds");
- 14
- for (Long goodsId: goodsIds2) {
- 15 System.out.println(goodsId);
- 16
- }
- 17
- }
这样做确实能够通过测试,但在实际开发中,用 Map<String, Object> 去接收到的参数,当数值小于 Integer 的最大值时,会默认将其按 Integer 处理。
来源: http://www.cnblogs.com/HarrisonHao/p/6137573.html