Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 401|回复: 0

记一次由于Java泛型类型擦除而导致的问题,及解决办法

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-1 00:08:59 | 显示全部楼层 |阅读模式

    中所周知,Java中的泛型并不像C++、C#一样是真正的泛型,其泛型是通过类型擦除来实现的。具体什么是类型擦除,可以参看这篇博文:http://icyfenix.iteye.com/blog/1021949。今天要记录的是在实际开发中遇到的一个,由于Java这种泛型的实现方式而导致的问题,及解决办法。

     

    一下代码是模拟真实开发环境下的实现:

     1   @Test
     2   public void test(){
     3     // 构建searchMap,模拟前端传来的查询参数
     4     Map<String, Object> searchMap = new HashMap<String, Object>();
     5     List<Integer> goodsIds1 = new ArrayList<Integer>();
     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<Long> goodsIds2 = (List<Long>)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<String, Object> searchMap = new HashMap<String, Object>();
     5     List<Long> goodsIds1 = new ArrayList<Long>();
     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<Long> goodsIds2 = (List<Long>)searchMap.get("goodsIds");
    14     for(Long goodsId : goodsIds2){
    15       System.out.println(goodsId);
    16     }
    17   }

    这样做确实能够通过测试,但在实际开发中,用Map<String, Object>去接收到的参数,当数值小于Integer的最大值时,会默认将其按Integer处理。

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-1 14:01 , Processed in 0.060092 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表