前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文带你读懂 Java 泛型

一文带你读懂 Java 泛型

原创
作者头像
大发明家
发布2021-12-21 12:29:59
3170
发布2021-12-21 12:29:59
举报
文章被收录于专栏:技术博客文章技术博客文章

一、泛型的使用和类型擦除

泛型本质上是参数化类型(paramentersized

type)的应用,可以声明在类、接口或者方法上。没有泛型时可以用Object结合强制类型转化实现相似需求,但是运行时风险就明显了,泛型就是用于解决这个问题的。

Java泛型并没有真的新增类型,List<String>和List<Int>在编译后都是List,依靠的是类型擦除和强制转换来实现的。

代码语言:txt
复制
Map<String, String> map = HashMap<String, String>()
代码语言:txt
复制
map.put("Hello", "你好")
代码语言:txt
复制
System.out.println(map.get("Hello"))

编译后

代码语言:txt
复制
Map map = HashMap()
代码语言:txt
复制
map.put("Hello", "你好")
代码语言:txt
复制
System.out.println((String) map.get("Hello"))

二、在字节码文件中的形式

这里只简单介绍下相关基础,Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中。

结构中只有无符号数和表两种结构,无符号数描述数字、索引引用、数量值等,表由无符号数和其它表组合。

文件中依次为Class版本、常量池、访问标志、父类信息、字段表、方法表等。类、字段表、方法表等都有属性表,在属性表中有个Signature属性就是用于记录泛型签名信息的。

属性表中有个Code属性,即是方法编译成后的字节码指令序列,泛型擦除仅仅是擦除Code表中的泛型信息。

三、在内存中的形式

直接指针模型,对象在堆内存中有对象头和对象实例两部分,对象头包括mark word和类型指针两部分。

Java内存模型

泛型信息以参数化类型信息(paramentersized

typ)的方式存储在对象类型实例(即方法区中的Class)中,各种泛型实例的类型指针都指向方法区中同一class类型实例。

代码语言:txt
复制
 //运行后可以发现两个class对象是同一个
代码语言:txt
复制
 val list1 = ArrayList<String>()
代码语言:txt
复制
 val list2 = ArrayList<Int>()
代码语言:txt
复制
 println(list1::class.hashCode().toString())
代码语言:txt
复制
 println(list2::class.hashCode().toString())

即使这段代码编译成 val list1 =

ArrayList(),字段list在其方法的字段表中,其中的属性表Signature属性记录着泛型是String还是Int。

四、配合反射获取泛型信息

我们定义一个泛型类

代码语言:txt
复制
   open class SuperClass<T> {
代码语言:txt
复制
    }
代码语言:txt
复制
    class TestClass: SuperClass<Double>() {
代码语言:txt
复制
        val name = ArrayList<Float>()
代码语言:txt
复制
        private fun <E> parameterizedFun(element: E): E {
代码语言:txt
复制
            return element
代码语言:txt
复制
        }
代码语言:txt
复制
        fun testParameter(list: List<String>) {
代码语言:txt
复制
        }
代码语言:txt
复制
    }

根据上面的分析可以知道从参数化类型信息中就可以得到真实类型

代码语言:txt
复制
    @Test
代码语言:txt
复制
    fun testFun() {
代码语言:txt
复制
        val clazz = TestClass::class.java
代码语言:txt
复制
        //获取类泛型
代码语言:txt
复制
        if (clazz.genericSuperclass is ParameterizedType) {
代码语言:txt
复制
            val parameterizedType = clazz.genericSuperclass as ParameterizedType
代码语言:txt
复制
            val parameterClass = parameterizedType.actualTypeArguments[0]  as Class<\*>
代码语言:txt
复制
            println(parameterClass)
代码语言:txt
复制
        }
代码语言:txt
复制
        //获取方法参数的泛型
代码语言:txt
复制
        val method = clazz.getMethod("testParameter", List::class.java)
代码语言:txt
复制
        if (method.genericParameterTypes[0] is ParameterizedType) {
代码语言:txt
复制
            val parameterizedType = method.genericParameterTypes[0] as ParameterizedType
代码语言:txt
复制
            val parameterClass = parameterizedType.actualTypeArguments[0]  as Class<\*>
代码语言:txt
复制
            println(parameterClass)
代码语言:txt
复制
        }
代码语言:txt
复制
        //获取变量类型
代码语言:txt
复制
        val field = clazz.getDeclaredField("name")
代码语言:txt
复制
        if (field.genericType is ParameterizedType) {
代码语言:txt
复制
            val parameterizedType = field.genericType as ParameterizedType
代码语言:txt
复制
            val parameterClass = parameterizedType.actualTypeArguments[0]  as Class<\*>
代码语言:txt
复制
            println(parameterClass)
代码语言:txt
复制
        }
代码语言:txt
复制
    }

最后输出

代码语言:txt
复制
class java.lang.Double
代码语言:txt
复制
class java.lang.String
代码语言:txt
复制
class java.lang.Float

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、泛型的使用和类型擦除
  • 二、在字节码文件中的形式
  • 三、在内存中的形式
  • 四、配合反射获取泛型信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档