今天我们来谈谈泛型。其实在初学的时候,我就对泛型有点蒙,因为看到有人说 Java 的泛型不是真的泛型,我搞不懂。
还有人说 Java 的泛型在实际运行时候会把类型给擦除了,我想着擦除是什么意思什么要擦除/p>
那把类型给擦除了为什么反射的时候还能得到泛型的类型信息/p>
我们今天就来盘一盘泛型:
- 为什么需要泛型/li>
- 为什么都说Java的泛型是伪泛型/li>
- 为什么Java泛型的实现是类型擦除/li>
- 既然擦除了类型,为什么在运行期仍能反射获得类型/li>
需要框架源码的朋友可以看我个人简介联系我。推荐分布式架构源码。
为什么需要泛型
我们都知道在 Java5 之前是没有泛型的,没泛型都能用的好好的,那为什么要加个泛型呢,能给我们带来什么呢/p>
我们先来看下下面这段代码:
在没有泛型的时候,加入的集合的数据并不会做任何约束,都会被当作成 Object 类型。
可能有人说,这很好呀,多自由!确实,自由是自由了,但是代码的约束能力越低,就越容易出错,使用上也有诸多不便,比如获取的时候需要强转。
如果一不小心取错类型,编译的时候能过,但是运行的时候却抛错。
而泛型的作用就是加了一层约束,约束了类型。
有了这一层约束就好办事儿了,由于声明了类型,可以在编译的时候就识别出不准确的类型元素。使得错误提早抛出,避免运行时才发现。
我们再小结一下泛型的好处:
- 提高了代码的可读性,一眼就能看出集合(其它泛型类)的类型
- 可在编译期检查类型安全,增加程序的健壮性
- 省心不需要强转(其实内部帮做了强转,下面会说)
- 提高代码的复用率,定义好泛型,一个方法(类)可以适配所有类型 (其实以前 Object 也行,就是比较麻烦)
为什么都说Java的泛型是伪泛型
看起来我们平日用的一些泛型好像没啥毛病啊什么都说Java的泛型是伪泛型里伪了/p>
我们再来看一段代码:
这说明在运行时泛型根本没有起作用!也就是说在运行的时候 JVM 获取不到泛型的信息,也会不对其做任何的约束。
你可以认为 Java 的泛型就是编译的时候生效,运行的时候没有泛型,所以大家才说 Java 是伪泛型!
因此,虽然在 IDE 写代码的时候泛型生效了,而实际上在运行的时候泛型的类型是被擦除的。
一言蔽之,Java的泛型只在编译时生效,JVM 运行时没有泛型。
为什么Java泛型的实现是类型擦除/h1>
类型擦除 (type Erasure)。
Java 之所以在运行时将类型擦除的原因是为了向下兼容,即兼容 Java5 之前的编译的 class 文件。
例如 Java 1.2 上正在跑的代码,可以在 Java 5 的 JRE 上运行。
就是为了这该死的向下兼容,才使得 Java 实现的是伪泛型。
我从现有的实现倒推伪泛型的设计可能思路(我个人瞎掰的,您随意听听)是这样的:
- 这 Java 5 以前的版本,线上已经有很多应用在跑了,我好像不能新加一套,影响推广还可能被骂的很惨
- 咋办,泛型毕竟是加一个约束,以前的代码没这个约束啊,该如何兼容/li>
- 有了,要不我在编译器上动手脚,在编译的时候识别和约束泛型,然后编译过了就把泛型的信息擦除了。这样运行的时候约束不是没了吗就和之前保持一致了吗,就这样干了!
总而言之,就是为了向下兼容才采用类型擦除来实现的。
这里还有个坑,也就是泛型不支持基本类型,比如 int。因为泛型擦除后就变成了Object,这个 int 和 Object 兼容有点麻烦。
我在 上看 R大的解释如下:
GJ / Java 5说:这个问题有点麻烦,赶不及在这个版本发布前完成了,就先放着不管吧。于是Java 5的泛型就不支持原始类型,而我们不得不写恶心的ArrayList<Integer>、ArrayList<Long>… <br>这就是一个偷懒了的地方。
emmm,这说明啥Java 的也是程序员,也是要发版有上线需求的,所以说……
好了,言归正传,现在 Java 的泛型实现确实是伪泛型。看到这不经有人会发问道就只能一直伪泛型了吗/p>
那啥,我觉得吧,只要时间允许,只要钱够,应该都能做哈哈。
既然擦除了类型,为什么在运行期仍能反射获得类型/h1>
难道是没擦干净急,我们慢慢看。
我们先来回顾一下这段代码:
我们定义了泛型类型为 String 的 list,并且获取的 str 不需要强转,这一步是怎么做的呢我们 看下字节码:
然后看到 #7 没,有个 ,强转的类型是 String,看到这大伙儿应该都明白,为什么类型擦除了,但是我们 get 的时候不需要强转呢strong>因为编译器隐性的帮我们插入了强转的代码!所以我们的 Java 代码中不需要写强转。
再回到此小节标题:既然擦除了类型,为什么在运行期仍能反射获得类型/p>
答案就藏在 class 文件中。我们来看下这段代码:
我们直接进行一手 ,反编译看到字节码里面有这样的记录:
也正因为原理如此,所以我们只能对以下三种情况利用反射获取泛型类型:
- 成员变量的泛型
- 方法入参的泛型
- 方法返回值的泛型
对于局部变量这种是无能为力的。
文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览92016 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!