概念介绍

Java语言的泛型采用的是擦除法实现的伪泛型,泛型信息(类型变量、参数化类型)编译之后通通被除掉了。使用擦除法的好处就是实现简单、非常容易Backport,运行期也能够节省一些类型所占的内存空间。

而擦除法的坏处就是,通过这种机制实现的泛型远不如真泛型灵活和强大。Java选取这种方法是一种折中,因为Java最开始的版本是不支持泛型的,为了兼容以前的库而不得不使用擦除法。

验证擦除,我们编写下面代码:

public class ErasedTypeEquivalence {
    public static void main(String[] args) {
    	//例1
        ArrayList<String> list1 = new ArrayList<String>();
        list1.add("abc");
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        list2.add(123);
        System.out.println(list1.getClass() == list2.getClass());//true
		
		//例2
		ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);  //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer
        list.getClass().getMethod("add", Object.class).invoke(list, "asd");
        for (int i = 0; i < list.size(); i  ) {
            System.out.println(list.get(i));//会输出1和asd
        }
    }
}

在例1中,我们定义了两个ArrayList数组,不过一个是ArrayList<String>泛型类型的,只能存储字符串;一个是ArrayList<Integer>泛型类型的,只能存储整数,最后,我们通过list1对象和list2对象的getClass()方法获取他们的类的信息,最后发现结果为true。说明泛型类型String和Integer都被擦除掉了,只剩下原始类型。       

在例2中,定义了一个ArrayList泛型类型实例化为Integer对象,如果直接调用add()方法,那么只能存储整数数据,不过当我们利用反射调用add()方法的时候,却可以存储字符串,这说明了Integer泛型实例在编译之后被擦除掉了,只保留了原始类型。

上面两次提到了原始类型,什么是原始类型?原始类型 就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

问题案例

最近在搭系统基础代码架构,其中就涉及到系统数据字典 功能,以前都是用varchar类型保存字典内容,这次准备玩点新花样,准备用上MySQL的JSON类型保存字典表的内容字段。>>文章传送门<<

实际操作之后就遇到了泛型擦除问题,如下图,我虽然对content字段的List指定了泛型DictContent,但是在做类型转换时,只能指定javaType=List,没有也不能指定其泛型:

在没有指定泛型的情况下,JacksonTypeHandler在做类型转换后生成的集合的泛型就与预期的不一致:

原因分析

原因很简单,在resultMap中指定的JavaType是java.util.List,此处只能指定类类型,并不能指定泛型。而在对应的类型转换类中也没有指定其泛型,而List<DictContent>和List<Object>的类类型是一样的,所以在给content字段赋值时是不会报错的。但是一旦你需要操作List的中的元素,在取出元素时,JVM就发现你要的类型是DictContent 而实际上是LinkedHashMap,就会抛出类型转换异常。

通俗的讲就是你准备买华为手机(将JSON类型转成List<DictContent>类型),但是买的时候没有说要买什么牌子的手机(在javaType中只指定了List类型,没有也无法指定泛型),而店子里有很多牌子的手机,所以店家就随便给了你一款手机。。。

以下是Mybatis Plus中的部分源码,可以看到在没有指定List的泛型的情况下,通过JacksonTypeHandler处理后的元素类型并不是我们预期的类型:

下图我们可以看到JacksonTypeHandler是BaseTypeHandler的子类,而且指定了BaseTypeHandler中的泛型是Object类型,但是上图中的泛型却是LinkedHashMap。

至于为什么是LinkedHashMap,我觉得是JVM指定的,如果哪位大佬比较清楚这块的逻辑还请在评论中指点一下!

解决方案

既然原因搞清楚了,解决方案就呼之欲出了,有两种方案:

  • 自定义一个指定泛型的集合类替代List<T>
  • 引用上文中通俗的说法,这个方案就是在买手机的时候告诉卖家,我要买华为手机。
  • 自定义一个指定泛型的TypeHandler类替代JacksonTypeHandler类
  • 而这里的的通俗的说法就是让店家只卖华为手机。

以上两种方案都可以实现我们的需求。

从工作量上来说,自定义一个List<T>显然更少,所以我选择了第一种方案,如图:

8.11新增:第二种解决方式:

替换后结果如下:

至此,泛型擦除问题解决。 

总结

不得不说,玩新花样总是会遇到各种各样的坑,但是编程之路,不就是不断的踩坑,不断的改BUG,积累经验,打怪升级。如果不是因为最近玩了这个新花样,可能我这辈子都不会遇到泛型擦除的问题!       

以上为个人经验,希望能给大家一个参考,也希望大家多多支持Devmax。

关于泛型擦除问题的解决--Mybatis查询类型转换的更多相关文章

  1. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  2. Springboot集成mybatis实现多数据源配置详解流程

    在日常开发中,若遇到多个数据源的需求,怎么办呢?通过springboot集成mybatis实现多数据源配置,简单尝试一下,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. PHP CodeIgniter分页实例及多条件查询解决方案(推荐)

    这篇文章主要介绍了PHP CodeIgniter分页实例及多条件查询的思路详解,非常不错,具有参考借鉴价值,需要的朋友可以参考下

  4. Mybatis结果集映射与生命周期详细介绍

    结果集映射指的是将数据表中的字段与实体类中的属性关联起来,这样 MyBatis 就可以根据查询到的数据来填充实体对象的属性,帮助我们完成赋值操作

  5. Java实现分页查询功能

    这篇文章主要为大家详细介绍了Java实现分页查询功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  6. Spring Boot 整合持久层之MyBatis

    在实际开发中不仅仅是要展示数据,还要构成数据模型添加数据,这篇文章主要介绍了SpringBoot集成Mybatis操作数据库,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  7. Mybatis详解动态SQL以及单表多表查询的应用

    MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑,下面这篇文章主要给大家介绍了关于Mybatis超级强大的动态SQL语句的相关资料,需要的朋友可以参考下

  8. PHP组合查询多条件查询实例代码第1/2页

    今天想向大家介绍PHP页面间如何进行多条件组合查询。在很多其它网站也有很多相关介绍,但都不够详尽,在这里,我将详细地为大家说明这一比较实用但又相当关键的技术。

  9. mybatis水平分表实现动态表名的项目实例

    本文主要介绍了mybatis水平分表实现动态表名的项目实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  10. thinkphp查询,3.X 5.0方法(亲试可行)

    下面小编就为大家带来一篇thinkphp查询,3.X 5.0方法(亲试可行)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部