java object转泛型_为什么Java的泛型要用擦除实现

在 Java 中的 泛型 ,常常被称之为 伪泛型 ,究其原因是因为在实际代码的运行中,将实际类型参数的信息擦除掉了 (Type Erasure) 。那是什么原因导致了 Java 做出这种妥协的呢?下面我就带着大家以 Java 语言设计者的角度,带领大家一起了解这里面的辛酸过往。

什么是真泛型

在了解 Java "伪泛型" 之前,我们先简单讲一讲"真泛型"与“伪泛型”的区别。

  • 真泛型:泛型中的类型是真实存在的。
  • 伪泛型:仅于编译时类型检查,在运行时擦除类型信息。

编程语言实现“真泛型”的一般思路

一般编程语言引入泛型的思路,都是通过 编译时膨胀法

以 Java 语言实现"真泛型"为例,首先,对 泛型类型(泛型类、泛型接口) 、 泛型方法 的名字使用特别的编码,例如将 Factory 类生成为一个名为 “Factory@@T” 的类,这种特别的编码后的名字将被编译器识别,作为判断是否为泛型的依据。方法中使用占位符 T 的地方可以临时生成为 Object ,并带上特别的 Annotation 让 Java 编译器能得知这个是占位符。然后,如果编译时发现有对 Factory 的使用,则将 “Factory@@T” 的所有逻辑复制一份,新建 “Factory@String@” 类,将原本的占位符 T 替换为 String 。然后在编译 new Factory() 时生成 new Factory@String@() 即可。

术语中文含义举例

Parameterized type参数化类型List

Actual type parameter实际类型参数String

Formal type parameter形式类型参数E

以 Factory 与 Factory 为例,其生成的代码为:

//替换前的代码Factory() f1 = new Factory();Factory() f2 = new Factory();//替换后的代码Factory() f1 = new Factory@String@()Factory() f2 = new Factory@Integer@();
  • 其中 Factory@String@ 类中的 T 已经被替换为 String。
  • 其中 Factory@Integer@ 类中的 T 已经被替换为 Integer。

因为含有不同的 实际类型参数 的 泛型类型 都被替换为了不同的类,且泛型类型中的类型也都得到了确认。所以我们在程序中可以这么做:

class Factory {    T data;    public static void f(Object arg){        if(arg instanceof T){...}//success        T var = new T();//success        T[] array = new T[10];//success    }}

Java 直接使用 “真泛型” 带来的问题

单从技术来说,Java 是完全 100% 能实现我们所说的 ”真泛型”。想要实现真泛型,有如下两件事Java 必须要处理:

  • 修改 JVM 源代码,让 JVM 能正确的的读取和校验泛型信息(之前的Java 是没有泛型这种概念的,所以需要修改)。
  • 为了兼容老程序,需为原本不支持泛型的 API 平行添加一套泛型 API(主要是容器类型)。

就拿 ArrayList 来说,也就是必须这么做:

ArrayList

即使以上的事情都做了,Java 也并不能采用这种方案。试想如下情况:

b260365e9fa8ecae6fb35bedcec68b8a.png

如果我有一个 Java 5 之下 的 A 项目与第三方的 B1 lib,其中有 A 项目中引用了 B1 lib 中的某个 ArrayList ,随着 Java 的升级,B1 lib 的开发者为了使用 Java 新特性--泛型,故将代码迁移到了 Java 5,并重新生成了 B2 lib,那么 A 项目要兼容 B2 lib,那么 A 项目中必须升级到 Java 5 并同时修改代码。

A 项目为什么必须要升级 Java 5?

在 Java 中不支持高版本的 Java 编译生成的 class 文件在低版本的 JRE 上运行,如果尝试这么做,就会得到 UnsupportedClassVersionError 错误。如下图所示:

413ee6ddc1d0b987a4bbf544d08e85e4.png

故 A 项目要适配 B2 lib,必要要把 Java 升级到 Java 5。

那现在我们再回过头来想想,Java 版本迭代都从 1.0 到 5.0了,有多少的开源框架,有多少项目,如果为了引入泛型,强行让开发者修改代码。这种情况,各位同学。自行脑补。估计数以万计的开发者拿着刀,在堵 Java 语言架构师的门吧。

逼不得已的类型擦除

在上节中,我们探讨了 Java 不能直接引入“真泛型” 的实际原因。因为“真泛型”的引入,势必会为原本不支持泛型的 API 平行添加一套泛型 API。而新增了API,对于 Java 开发者来说,又必须要做迁移。

那还有什么方案,能让开发者平滑的过渡到 Java 5, 又能使用泛型新特性呢?

06020d0fad439c0dc35273eae367f578.png

有的,有的。Java 如果想摆脱用户新版本的迁移问题。Java 必要要做以下两件事情:

  • 不再新增一套泛型 API, 直接把已有的类型原地泛型化
  • 处理泛化前后类型的兼容。

下面我们分别探讨一下这两件事做的目的及其原因。

做第一件事,是保证了开发者不会因为 Java 的升级,而对以前的老代码进行修改,以 ArrayList 为例, 直接在原有包(java.util)下进行修改 ,也就是这样:

//:point_down:Java老版本class ArrayList{}//:point_down:Java5泛型版本class ArrayList{}

做第二件事的目的,还是以我们之前的例子进行分析,在 A 项目中,A 项目引用了 B1 lib 中的 ArrayList(用 list 变量记录),那么假设 A 项目升级到 Java 5 后,还是引用的 B1 lib,那么必然会出现如下这种情况:

下述代码中,A 项目将泛化后 ArrayList 的传递给了 B1 lib 中的 ArrayList 。

ArrayList list = new ArrayList();
ArrayList

这种情况的出现,会导致一个问题。就是 b1 项目中的 ArrayList 是不知道 A 项目中的 Arraylist 已经泛型化了的,那么如何保证 泛型化后 的 ArrayList( 也就是ArrayList )与 老版本 的 ArrayList 等价呢?

如果按照我们之前讲解的 “真泛型” 思路来处理 Java 的泛型, 那么 new ArrayList() 实际会被替换为 new ArrayList@String@() ,那么实际运行代码是这样:

ArrayList list = new Factory@String@()复制代码

从代码逻辑上来看,根本就跑不通。因为 ArrayList 与 ArrayList@String@ 根本就不是同一类, 那怎么办呢?

最为直接的解决方案就是,不再为参数化类型创造新类了,同时在编译期间将 泛型类型 中的 类型参数 全部替换 Object (因为不创建新类了,那么在泛型类中的 T 对应的类型,只能用 Object 替换)。

在 Java 的泛型实际实现中,会根据泛型类型中的类型参数有无边界,来选择是否替换为边界或 Object。

举个例子:

ba485a3bdd095947fe9328168725cd5d.png

在上述代码中,声明了一个泛型类型 Node ,在编译器替换后,实际为 Node。也就是这样:

//编译器的代码 Node node = new Node(); //编译后的代码 Node node = new Node();

通过编译器的”魔法“,Java 就解决了处理泛型兼容老版本的问题。

总结

阅读到这里,我相信大家已经明白了 Java 中的泛型为什么要擦除类型信息了。虽然 Java 的 "伪泛型“ 一直被其他编程语言所歧视,但不管怎样,兼容老版本这种行为,也是一件值得尊敬以及认可的一件事。

不管 Java 做了什么,总是功大于过的。这里推荐一个视频给大家。相信看了这个视频之后,大家会知道 Java 对于整个世界的重要性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java lambda函数_Java SE 8新功能介绍:使用Lambda Expression进行函数式编程

java lambda函数“ Java SE 8新功能浏览 ”系列的这篇文章将深入了解Lambda表达式 。 我将向您展示Lambda表达式的几种不同用法。 它们都具有功能接口的共同实现。 我将解释编译器如何从代码中推断信息,例如特定类型的变量以及后台实际发生的情况。 在上一篇文章“…

Apache Camel 3.1 –更多骆驼核心优化(第2部分)

我以前曾在博客中介绍我们在下一个Camel 3.1版本(第1部分)中所做的优化 。 今天,我想发布大约4周后的最新状态更新。 我们集中在三个方面优化骆驼核心: 不必要的对象分配 不必要的方法调用 提高绩效 换句话说,我…

计算机系统的指令系统,计算机指令系统指的是什么呢?

2014-12-27计算机系统的指令由你哪两部分组成?共作用分别是什么?1。8086汇编语言指令由标号、操作码、操作数和注释组成,其中标号和注释可以省略,操作码指出指令要过盛的功能,操作数指出完成的对象。2。变量和标号的区别是变量由伪指令定义&a…

echarts怎么保存图片到剪切板上_在电脑上怎么批量给图片编号以及怎么自动记录记事本txt文档时间...

电脑日益成为我们日常办公不可或缺的工具,除了必要的软件使用之外,也有一些直到今天还不那么为人所熟知的小技巧。而小编今天就暂时为大家介绍两个颇为常用的小技巧~分别是如何将图片批量编号以及自动记录记事本时间。技巧一、图片批量编号旅行或者活动结…

计算机类qq网名,最帅的qq名字

qq这个聊天工具已经成为人们电脑里必备的软件,上到40~50的叔叔阿姨,下至8~9岁的小学生,都有qq这个聊天工具。qq是有可以随意改名字的功能,最长可以输入十几个字符作为名字。就是因为取名没有太多的限制,反而让人不知道…

c++中求解非线性方程组_齐次线性方程组的基础解系的简便算法

线性方程组的求解是线性代数中的基本技能,而齐次线性方程组的基础解系的求法又是基础。本文给出一个计算齐次线性方程组的基础解系的公式,从而简化计算过程。01 符号说明 n元线性方程组的矩阵形式:(1)齐次线性方程组;(2)非齐次线性方程组;系数…

python的if语句例句_Python入门之if条件语句

Besides the while statement just introduced, Python knows the usual control flow statements known from other languages, with some twists.除了之前介绍的while语句,Python同样支持其他语言通常用的控制流语句,但也有一些区别。 if Statements P…

esp32 怎么分配freertos 堆栈大小_深度解剖~ FreeRtos阅读笔记2 任务创建、内核链表初始化...

2.FREERTOS任务创建、内核链表初始化硬件环境:cortex m4FreeRTOS版本:v8.0.1今天开始阅读freertos,阅读同时做下笔记,等哪天碰到移植问题再翻出来看看。2.1 任务、链表结构体源码中使用tskTCB来存储一个任务的所有信息,xLIST存储内…

Sigma IDE现在支持Python无服务器Lambda函数!

想想无服务器,使用Pythonic –全部在您的浏览器中! (好吧,这则新闻已经过了几周了,但是仍然……) 如果您沉迷于整个无服务器的“事物”中 ,您可能已经注意到我们,一个在SLAppForge臭…

idle不是python自带的开发工具_Python的开发工具

通常情况下,为了提高开发效率,需要使用相应的开发工具。进行Python开发也可以使用开发工具。下面将详细介绍Python自带的IDLE 一使用自带的IDLE 在安装Python后,会自动安装一个IDLE。它是一个Python shell(可以在打开的IDLE窗口的标题栏上看到…

java se 导原码_Java SE 8新功能导览:Java开发世界中的重大变化

java se 导原码我很自豪,像其他专业团队成员一样,是采用OpenJDK的成员之一,但是从过去8个月就加入了,我们经历了Java SE 8 开发,编译,编码,讨论等各个阶段,直到将其付诸实践为止。 。…

linux将日期和日历信息追加到文件中_Linux任务调度

crontab 任务调度crontab 进行定时任务的设置概述 任务调度:是指系统在某个时间执行的特定的命令或程序。任务调度分类:1.系统工作:有些重要的工作必须周而复始地执行,如病毒扫描等 。2.个别用户工作:个别用户可能希望…

android sdk build-tools_从零开始仿写一个抖音App——视频编辑SDK开发(一)

本文首发于微信公众号——世界上有意思的事,搬运转载请注明出处,否则将追究版权责任。交流qq群:859640274。大家好久不见,又有一个多月没有发文章了。不知道还有哪些读者记得我的 从零开始仿写抖音App 的系列文章,这个…

爱默生E系列服务器机柜托盘,艾默生通信电源PS48300-3B/1800 一体化室内机柜

PS48300-3B/1800电源系统PS48300-3B/1800电源系统是艾默生网络能源集多年开发和网上运行经验,采用 DSP控制技术,为满足3G网络需求而设计的高可靠、高功率密度、高性能、全数 字化通信电源系统。根据交流配电和机柜高度。一、特点 1、休眠节能专利技术&am…

功能Java示例 第8部分–更多纯函数

这是第8部分,该系列的最后一部分称为“ Functional Java by Example”。 我在本系列的每个部分中发展的示例是某种“提要处理程序”,用于处理文档。 在上一期文章中,我们已经使用Vavr库看到了一些模式匹配,并且还将故障也视为数据…

tensorflow 小于_坐姿不对,屏幕就变模糊!教你用TensorFlow做一款“隐形背背佳”...

大数据文摘出品作为一个上班族,每天坐在电脑前那么久,难免出现腰酸背痛的情况,时间长了甚至脊柱都歪曲变形了,这可不行!一定要克制住自己的坐姿。这里有款“隐形背背佳”,要不要了解一下?一位名…

python里随机抽取样本_概率分布和抽样分布基础知识及Python实现

本文主要介绍推论统计中的概率分布和抽样分布,本文结构如下: 一、概率分布 随机变量:在一定条件下,某件事情可能发生或者不发生,这个事件就叫随机事件。例如抛硬币哪面朝上。随机变量X就是用来量化随机事件的函数,是将随机事件每一个可能出现的结果映射到数值的一个函数。…

php 创建目录_使用SMB绕过PHP远程文件包含限制

译文声明本文是翻译文章,文章原作者mannulinux,文章来源:mannulinux.org 原文地址:http://www.mannulinux.org/2019/05/exploiting-rfi-in-php-bypass-remote-url-inclusion-restriction.html译文仅供参考,具体内容表达…

eas系统服务器地址,EAS7.0EAS7.5服务端及其客户端标准配置介绍

金蝶EAS是基于JavaEE的大型企业应用。目前服务端支持以下主流软硬件环境。适用版本:EAS7.0 EAS703 EAS7.51.金蝶EAS服务端支持的软硬件环境注:标志为部署支持的环境,可以正常安装配置,但是正式运行前需联系金蝶技术支持。2.客户端…

Java的Kafka:构建安全,可扩展的消息传递应用程序

使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。 今天尝试Okta。 当今的用户希望可以通过其计算机,手机,平板电脑或任何其他设备访问您的应用程序! …