文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

你,可能没完全搞懂 Java 泛型

2024-12-02 15:38

关注

本文转载自微信公众号「yes的练级攻略」,作者是yes呀。转载本文请联系yes的练级攻略公众号。

大家好,我是yes。

今天我们来谈谈泛型。其实在初学的时候,我就对泛型有点蒙,因为看到有人说 Java 的泛型不是真的泛型,我搞不懂。

还有人说 Java 的泛型在实际运行时候会把类型给擦除了,我想着擦除是什么意思?为什么要擦除?

那把类型给擦除了为什么反射的时候还能得到泛型的类型信息?

我们今天就来盘一盘泛型:

话不多说,发车!

为什么需要泛型

我们都知道在 Java5 之前是没有泛型的,没泛型都能用的好好的,那为什么要加个泛型呢,能给我们带来什么呢?

我们先来看下下面这段代码:

  1. List list = new ArrayList(); 
  2.  list.add("yes"); // 加入string 
  3.  list.add(233); // 加入int 

在没有泛型的时候,加入的集合的数据并不会做任何约束,都会被当作成 Object 类型。

可能有人说,这很好呀,多自由!确实,自由是自由了,但是代码的约束能力越低,就越容易出错,使用上也有诸多不便,比如获取的时候需要强转。

如果一不小心取错类型,编译的时候能过,但是运行的时候却抛错。

综上,Java 引入了泛型。

而泛型的作用就是加了一层约束,约束了类型。

有了这一层约束就好办事儿了,由于声明了类型,可以在编译的时候就识别出不准确的类型元素。使得错误提早抛出,避免运行时才发现。

并且也不需要在代码上显示的强转,从以下代码可以看出,能直接获取 String 类型元素。

我们再小结一下泛型的好处:

提高了代码的可读性,一眼就能看出集合(其它泛型类)的类型

可在编译期检查类型安全,增加程序的健壮性

省心不需要强转(其实内部帮做了强转,下面会说)

提高代码的复用率,定义好泛型,一个方法(类)可以适配所有类型 (其实以前 Object 也行,就是比较麻烦)

为什么都说Java的泛型是伪泛型

看起来我们平日用的一些泛型好像没啥毛病啊?为什么都说Java的泛型是伪泛型?哪里伪了?

我们再来看一段代码:

可以看到,我声明的是一个 String 类型的集合,但是通过反射往集合中插入了 int 类型的数据,居然成功了???

这说明在运行时泛型根本没有起作用!也就是说在运行的时候 JVM 获取不到泛型的信息,也会不对其做任何的约束。

你可以认为 Java 的泛型就是编译的时候生效,运行的时候没有泛型,所以大家才说 Java 是伪泛型!

因此,虽然在 IDE 写代码的时候泛型生效了,而实际上在运行的时候泛型的类型是被擦除的。

一言蔽之,Java的泛型只在编译时生效,JVM 运行时没有泛型。

为什么Java泛型的实现是类型擦除?

类型擦除 (type Erasure)。

Java 之所以在运行时将类型擦除的原因是为了向下兼容,即兼容 Java5 之前的编译的 class 文件。

例如 Java 1.2 上正在跑的代码,可以在 Java 5 的 JRE 上运行。

就是为了这该死的向下兼容,才使得 Java 实现的是伪泛型。

我从现有的实现倒推伪泛型的设计可能思路(我个人瞎掰的,您随意听听)是这样的:

总而言之,就是为了向下兼容才采用类型擦除来实现的。

这里还有个坑,也就是泛型不支持基本类型,比如 int。因为泛型擦除后就变成了Object,这个 int 和 Object 兼容有点麻烦。

我在网上看 R大的解释如下:

GJ / Java 5说:这个问题有点麻烦,赶不及在这个版本发布前完成了,就先放着不管吧。于是Java 5的泛型就不支持原始类型,而我们不得不写恶心的ArrayList、ArrayList…

这就是一个偷懒了的地方。

emmm,这说明啥?写 Java 的也是程序员,也是要发版有上线需求的,所以说......

好了,言归正传,现在 Java 的泛型实现确实是伪泛型。看到这不经有人会发问?难道就只能一直伪泛型了吗?

那啥,我觉得吧,只要时间允许,只要钱够,应该都能做?哈哈哈。

既然擦除了类型,为什么在运行期仍能反射获得类型?

难道是没擦干净?别急,我们慢慢看。

我们先来回顾一下这段代码:

我们定义了泛型类型为 String 的 list,并且获取的 str 不需要强转,这一步是怎么做的呢?我们 javap -c 看下字节码:

我们从反编译看生成的字节码可以看到, new 的 list 没有保存泛型的信息,所以是被擦除了。

然后看到 #7 没,有个 checkcast ,强转的类型是 String,看到这大伙儿应该都明白,为什么类型擦除了,但是我们 get 的时候不需要强转呢?因为编译器隐性的帮我们插入了强转的代码!所以我们的 Java 代码中不需要写强转。

再回到此小节既然擦除了类型,为什么在运行期仍能反射获得类型?

答案就藏在 class 文件中。我们来看下这段代码:

通过反射,我确实获得了 list 的类型。那既然类型被擦除了,这又是怎么做到的呢?

我们直接进行一手 javap -v,反编译看到字节码里面有这样的记录:

这下很好理解了,class 文件里面存了这个信息,所以我们通过反射自然而然的就能得到这个类型。没错,就是这么简单。

也正因为原理如此,所以我们只能对以下三种情况利用反射获取泛型类型:

对于局部变量这种是无能为力的。

最后

好了,今天关于泛型的文章暂时先到这,其实泛型的东西还没讲完,比如通配符、上界下界的限制(泛型的 PECS 原则),再如泛型的桥接,以及桥接的坑。

东西还挺多的,所以放下篇!等着哈。

参考

 

https://www.zhihu.com/question/28665443/answer/118148143

 

来源:yes的练级攻略内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯