深入理解Java泛型

我们都知道Java泛型的实现依赖于类型擦除,但是不可能完全擦除,因为运行时还是要知道泛型的真实类型的,那这个真是类型是如何存储的,又如何能拿到?顺着这个问题可以把Java内存划分、类文件结构和对象内存布局串联起来。

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

泛型本质上是参数化类型(paramentersized type)的应用,可以声明在类、接口或者方法上。没有泛型时可以用Object结合强制类型转化实现相似需求,但是运行时风险就明显了,泛型就是用于解决这个问题的。
Java泛型并没有真的新增类型,List<String>和List<Int>在编译后都是List,依靠的是类型擦除和强制转换来实现的。

Map<String, String> map = HashMap<String, String>()
map.put("Hello", "你好")
System.out.println(map.get("Hello"))

编译后

Map map = HashMap()
map.put("Hello", "你好")
System.out.println((String) map.get("Hello"))

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

这里只简单介绍下相关基础,Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中。
结构中只有无符号数和表两种结构,无符号数描述数字、索引引用、数量值等,表由无符号数和其它表组合。
文件中依次为Class版本、常量池、访问标志、父类信息、字段表、方法表等。类、字段表、方法表等都有属性表,在属性表中有个Signature属性就是用于记录泛型签名信息的。
属性表中有个Code属性,即是方法编译成后的字节码指令序列,泛型擦除仅仅是擦除Code表中的泛型信息。

三、在内存中的形式

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


Java内存模型

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

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

即使这段代码编译成 val list1 = ArrayList(),字段list在其方法的字段表中,其中的属性表Signature属性记录着泛型是String还是Int。

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

我们定义一个泛型类

   open class SuperClass<T> {

    }

    class TestClass: SuperClass<Double>() {
        val name = ArrayList<Float>()

        private fun <E> parameterizedFun(element: E): E {
            return element
        }

        fun testParameter(list: List<String>) {
        }
    }

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

    @Test
    fun testFun() {
        val clazz = TestClass::class.java

        //获取类泛型
        if (clazz.genericSuperclass is ParameterizedType) {
            val parameterizedType = clazz.genericSuperclass as ParameterizedType
            val parameterClass = parameterizedType.actualTypeArguments[0]  as Class<*>
            println(parameterClass)
        }

        //获取方法参数的泛型
        val method = clazz.getMethod("testParameter", List::class.java)
        if (method.genericParameterTypes[0] is ParameterizedType) {
            val parameterizedType = method.genericParameterTypes[0] as ParameterizedType
            val parameterClass = parameterizedType.actualTypeArguments[0]  as Class<*>
            println(parameterClass)
        }

        //获取变量类型
        val field = clazz.getDeclaredField("name")
        if (field.genericType is ParameterizedType) {
            val parameterizedType = field.genericType as ParameterizedType
            val parameterClass = parameterizedType.actualTypeArguments[0]  as Class<*>
            println(parameterClass)
        }
    }

最后输出

class java.lang.Double
class java.lang.String
class java.lang.Float
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • [TOC] 深入理解 Java 泛型 概述 泛型的本质是参数化类型,通常用于输入参数、存储类型不确定的场景。相比于...
    albon阅读 5,285评论 0 7
  • 概述 泛型的本质是参数化类型,通常用于输入参数、存储类型不确定的场景。相比于直接使用 Object 的好处是:编译...
    彳亍口巴阅读 124评论 0 3
  • 简介 泛型的意思就是参数化类型,通过使用参数化类型创建的接口、类、方法,可以指定所操作的数据类型。比如:可以使用参...
    零度沸腾_yjz阅读 3,306评论 1 15
  • 📓 本文已归档到:「javacore」🔁 本文中的示例代码已归档到:「javacore」 1. 为什么需要泛型 J...
    静默虚空阅读 169评论 1 1
  • 说来惭愧,虽然平时经常会使用到一些泛型类,但是却一直没有深入地去了解过泛型机制。今天开始学习记录泛型机制相关的知识...
    怡红快绿阅读 524评论 0 1