返回顶部
首页 > 资讯 > 精选 >Java泛型的实现方式是什么
  • 251
分享到

Java泛型的实现方式是什么

2023-06-16 11:06:39 251人浏览 独家记忆
摘要

本篇内容主要讲解“Java泛型的实现方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java泛型的实现方式是什么”吧!Java 泛型实现方式Java 采用**类型擦除(Type eras

本篇内容主要讲解“Java泛型的实现方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java泛型的实现方式是什么”吧!

Java 泛型实现方式

Java 采用**类型擦除(Type erasure  generics)**的方式实现泛型。用大白话讲就是这个泛型只存在源码中,编译器将源码编译成字节码之时,就会把泛型『擦除』,所以字节码中并不存在泛型。

对于下面这段代码,编译之后,我们使用 javap -s class 查看字节码。

Java泛型的实现方式是什么

方法源码

Java泛型的实现方式是什么


字节码

观察setParam 部分的字节码,从 descriptor 可以看到,泛型 T 已被擦除,最终替换成了 Object。

“ps:并不是每一个泛型参数被擦除类型后都会变成 Object 类,如果泛型类型为 T extends String 这种方式,最终泛型擦除之后将会变成  String。

同理getParam 方法,泛型返回值也被替换成了 Object。

为了保证 String param = genericType.getParam(); 代码的正确性,编译器还得在这里插入类型转换。

除此之外,编译器还会对泛型安全性防御,如果我们往 ArrayList添加 Integer,程序编译期间就会报错。

最终类型擦除后的代码等同与如下:

Java泛型的实现方式是什么

类型擦除带来的缺陷

作为对比,我们再来简单聊下 C# 泛型的实现方式。

**C#**泛型实现方式为「具现化式泛型(Reifiable generics)」,不熟悉的  C#小伙伴可以不用纠结具现化技术概念,我也不了解这些特性--!

简单点来讲,**C#**实现的泛型,无论是在程序源码,还是在编译之后的,甚至是运行期间都是切实存在的。

相对比与 C# 泛型,Java 泛型看起来就像是个「伪」泛型。Java 泛型只存在程序源码中,编译之后就被擦除,这种缺陷相应的会带来一些问题。

不支持基本数据类型

泛型参数被擦除之后,强制变成了 Object 类型。这么做对于引用类型来说没有什么问题,毕竟 Object 是所有类型的父类型。但是对于 int/long  等八个基本数据类型说,这就难办了。因为 Java 没办法做到int/long 与 Object 的强制转换。

如果要实现这种转换,需要进行一系列改造,改动难度还不小。所以当时 Java  给出一个简单粗暴的解决方案:既然没办法做到转换,那就索性不支持原始类型泛型了。

如果需要使用,那就规定使用相关包装类的泛型,比如  ArrayList。另外为了开发人员方便,顺便增加了原生数据类型的自动拆箱/装箱的特性。

正是这种「偷懒」的做法,导致现在我们没办法使用原始类型泛型,又要忍受包装类装箱/拆箱带来的开销,从而又带来运行效率的问题。

运行效率

上面字节码例子我们已经看到,泛型擦除之后类型将会变成 Object。当泛型出现在方法输入位置的时候,由于 Java  是可以向上转型的,这里并不需要强制类型转换,所以没有什么问题。

但是当泛型参数出现在方法的输出位置(返回值)的时候,调用该方法的地方就需要进行向下转换,将 Object 强制转换成所需类型,所以编译器会插入一句  checkcast 字节码。

除了这个,上面我们还说到原始基本数据类型,编译器还需帮助我们进行装箱/拆箱。

所以对于下面这段代码来说:

List<Integer> list = new ArrayList<Integer>(); list.add(66); // 1 int num = list.get(0); // 2

对于①处,编译器要做就是增加基本类型的装箱即可。但是对于第二步来说,编译器首先需要将 Object 强制转换成  Integer,接着编译器还需要进行拆箱。

类型擦除之后,上面代码等同于:

List list = new ArrayList(); list.add(Integer.valueOf(66)); int num = ((Integer) list.get(0)).intValue();

如果上面泛型代码在 C# 实现,就不会有这么多额外步骤。所以 Java 这种类型擦除式泛型实现方式无论使用效果与运行效率,还是全面落后于 C#  的具现化式泛型。

运行期间无法获取泛型实际类型

由于编译之后,泛型就被擦除,所以在代码运行期间,Java 虚拟机无法获取泛型的实际类型。

下面这段代码,从源码上两个 List 看起来是不同类型的集合,但是经过泛型擦除之后,集合都变为 ArrayList。所以  if语句中代码将会被执行。

ArrayList<Integer> li = new ArrayList<Integer>(); ArrayList<Float> lf = new ArrayList<Float>(); if (li.getClass() == lf.getClass()) { // 泛型擦除,两个 List 类型是一样的     System.out.println("6666"); }

这样代码看起来就有点反直觉,这对新手来说不是很友好。

另外还会给我们在实际使用中带来一些限制,比如说我们没办法直接实现以下代码:

Java泛型的实现方式是什么

最后再举个例子,比如说我们需要实现一个泛型 List 转换成数组的方法,我们就没办法直接从 List 去获取泛型实际类型,所以我们不得不额外再传入一个  Class 类型,指定数组的类型:

public static <E> E[] convert(List<E> list, Class<E> componentType) {     E[] array = (E[]) Array.newInstance(componentType, list.size());     .... }

从上面的例子我们可以看到,Java 采用类型擦除式实现泛型,缺陷很多。那为什么 Java 不采用 C#  的那种泛型实现方式?或者说采用一种更好实现方式?

这个问题等我们了解 Java 泛型机制的历史,以及当时 Java 语言的现状,我们才能切身体会到当时 Java 采用这种泛型实现方式的原因。

Java 泛型历史背景

Java 泛型最早是在 jdk5 的时候才被引入,但是泛型思想最早来自来自 c++ 模板(template)。1996 年  Martin Odersky(Scala 语言缔造者) 在刚发布的 Java 的基础上扩展了泛型、函数式编程等功能,形成一门新的语言-「Pizza」。

后来,Java 核心开发团队对 Pizza 的泛型设计深感兴趣,与 Martin 合作,一起合作开发的一个新的项目「Generic  Java」。这个项目的目的是为了给 Java 增加泛型支持,但是不引入函数式编程等功能。最终成功在 Java5 中正式引入泛型支持。

Java泛型的实现方式是什么

泛型移植过程,一开始并不是朝着类型擦除的方向前进,事实 Pizza 中泛型更加类似于 C# 中的泛型。

但是由于 Java 自身特性,自带严格的约束,让 Martin 在Generic Java 开发过程中,不得不放弃了 Pizza 中泛型设计。

这个特性就是,Java 需要做到严格的向后兼容性。也就是说一个在 JDK1.2 编译出来 Class 文件,不仅能在 JDK 1.2  能正常运行,还得必须保证在后续 JDK,比如 JDK12 中也能保证正常的运行。

这种特性是明确写入 Java 语言规范的,这是一个对 Java 使用者的一个严肃承诺。

“这里强调一下,这里的向后兼容性指的是二进制兼容性,并不是源码兼容性。也不保证高版本的 Class 文件能够运行在低版本的 JDK 上。

现在困难点在于,Java 1.4.2 之前都没有支持泛型,而 Java5 之后突然要支持泛型,还要让 JDK1.4  之前编译的程序能在新版本中正常运行,这就意味着以前没有的限制,就不能突然增加。

举个例子:

ArrayList arrayList=new ArrayList(); arrayList.add("6666"); arrayList.add(Integer.valueOf(666));

没有泛型之前, List 集合是可以存储不同类型的数据,那么引入泛型之后,这段代码必须的能正确运行。

为了保证这些旧的 Clas 文件能在 Java5 之后正常运行,设计者基本有两条路:

  1. 需要泛型化的容器(主要是容器类型),以前有的保持不变,平行增加一套新的泛型化的版本。

  2. 直接把已有的类型原地泛型化,不增加任何新的已有类型的泛型版本。

如果 Java 采用第一条路实现方式,那么现在我们可能就会有两套集合类型。以 ArrayList 为例,一套为普通的  java.util.ArrayList,一套可能为 java.util.generic.ArrayList

采用这种方案之后,如果开发中需要使用泛型特性,那么直接使用新的类型。另外旧的代码不改动,也可以直接运行在新版本 JDK 中。

这套方案看起来没什么问题,实际上C# 就是采用这套方案。但是为什么 Java 却没有使用这套方案那?

这是因为当时 C# 才发布两年,历史代码并不多,如果旧代码需要使用泛型特性,改造起来也很快。但是 Java 不一样,当时 Java  已经发布十年了,已经有很多程序已经运行部署在生产环境,可以想象历史代码非常多。

如果这些应用在新版本 Java 需要使用泛型,那就需要做大量源码改动,可以想象这个开发工作量。

另外 Java 5 之前,其实我们就已经有了两套集合容器,一套为 Vector/Hashtable 等容器,一套为 ArrayList/  HashMap。这两套容器的存在,其实已经引来一些不便,对于新接触的 Java 的开发人员来说,还得学习这两者的区别。

如果此时为了泛型再引入新类型,那么就会有四套容器同时并存。想想这个画面,一个新接触开发人员,面对四套容器,完全不知道如何下手选择。如何 Java  真的这么实现了,想必会有更多人吐槽 Java。

所以 Java 选择第二条路,采用类型擦除,只需要改动 Javac 编译器,不需要改动字节码,不需要改动虚拟机,也保证了之前历史没有泛型的代码还可以在新的  JDK 中运行。

但是第二条路,并不代表一定需要使用类型擦除实现,如果有足够时间好好设计,也许会有更好的方案。

当年留下的技术债,现在只能靠 Valhalla 项目来还了。这个项目从2014 年开始立项,原本计划在 JDK10  中解决现有语言的各种缺陷。但是结果我们也知道了,现在都 JDK14 了,还只是完成少部分目标,并没有解决核心目标,可见这个改动的难度啊。

到此,相信大家对“Java泛型的实现方式是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: Java泛型的实现方式是什么

本文链接: https://www.lsjlt.com/news/283430.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java泛型的实现方式是什么
    本篇内容主要讲解“Java泛型的实现方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java泛型的实现方式是什么”吧!Java 泛型实现方式Java 采用**类型擦除(Type eras...
    99+
    2023-06-16
  • Java泛型实现方式是什么
    这篇文章主要讲解了“Java泛型实现方式是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java泛型实现方式是什么”吧!Java 泛型实现方式Java 采用**类型擦除(Type era...
    99+
    2023-06-16
  • Java泛型的设计方法是什么
    这篇文章主要讲解了“Java泛型的设计方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java泛型的设计方法是什么”吧!引言泛型是Java中一个非常重要的知识点,在Java集合类框架...
    99+
    2023-06-17
  • java泛型指的是什么
    这篇文章主要讲解了“java泛型指的是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java泛型指的是什么”吧!概念泛型是指类型可以作为参数传递,本质上是类型参数。例如,当我们定义一种方...
    99+
    2023-06-30
  • java泛型方法是什么意思
    这篇文章主要讲解了“java泛型方法是什么意思”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java泛型方法是什么意思”吧!说明泛型方法,是在调用方法的时候指明泛型的具体类型。泛型方法可以在...
    99+
    2023-06-20
  • java中什么是泛型
    本篇文章给大家分享的是有关java中什么是泛型,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java的优点是什么1. 简单,只需理解基本的概念,就可以编写适合于各种情况的应用程...
    99+
    2023-06-14
  • Java中类型擦除式泛型的作用是什么
    本篇文章给大家分享的是有关Java中类型擦除式泛型的作用是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java选择的泛型类型叫做类型擦除式泛型。什么是类型擦除式泛型呢?就...
    99+
    2023-06-20
  • Java泛型的作用是什么
    这篇“Java泛型的作用是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java泛型的作用是什么”文章吧。简介泛型的作用...
    99+
    2023-06-29
  • Java泛型的特性是什么
    本篇内容介绍了“Java泛型的特性是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!泛型概述泛型在java中有很重要的地位,在面向对象编程...
    99+
    2023-06-02
  • Java泛型擦除是什么
    这篇文章主要介绍了Java泛型擦除是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java泛型擦除是什么文章都会有所收获,下面我们一起来看看吧。泛型信息只存在于代码编译阶段,但是在java的运行期(已经生成...
    99+
    2023-06-27
  • java中的泛型指的是什么
    这篇文章主要讲解了“java中的泛型指的是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java中的泛型指的是什么”吧!目录一、什么是泛型二、语法三、示例简单示例返回最大值-支持各种数据...
    99+
    2023-06-20
  • java泛型方法指什么
    这篇文章主要讲解了“java泛型方法指什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java泛型方法指什么”吧!目录什么是泛型泛型的使用规则泛型应用实例1、什么是泛型泛型,就是允许在定义...
    99+
    2023-06-20
  • Java中的泛型是什么意思
    这篇文章主要介绍“Java中的泛型是什么意思”,在日常操作中,相信很多人在Java中的泛型是什么意思问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中的泛型是什么意思”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-03
  • Java泛型模拟scala实现自定义ArrayList方式
    目录泛型模拟scala实现自定义ArrayList自定义实现ArrayList代码泛型模拟scala实现自定义ArrayList 泛型就是将类型由原来的具体的类型参数化,类似于方法中...
    99+
    2024-04-02
  • java泛型转换成对象的方法是什么
    Java中将泛型转换成对象的方法是使用类型擦除和强制类型转换。泛型在编译时会进行类型擦除,即将泛型类型转换成其上界类型(Object...
    99+
    2023-08-16
    java
  • java为什么不能实现真正泛型
    Java 之所以不能实现真正泛型的原因有以下几点:1. Java泛型是通过类型擦除来实现的,即在编译期间将泛型类型擦除为其上界或Ob...
    99+
    2023-09-20
    java
  • java中泛型Generic的作用是什么
    java中泛型Generic的作用是什么?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1. 背景泛型技术诞生之前(JDK5以前),创建集合的类型都是Object 类型的元素...
    99+
    2023-06-15
  • java使用泛型的优势是什么
    这篇文章主要介绍“java使用泛型的优势是什么”,在日常操作中,相信很多人在java使用泛型的优势是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java使用泛型的优势是什么”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-20
  • java泛型的局限是什么意思
    本篇内容主要讲解“java泛型的局限是什么意思”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java泛型的局限是什么意思”吧!1、泛型类的静态上下文中类型变量失效。  &nb...
    99+
    2023-06-20
  • java泛型如何实现
    在Java中,泛型是通过使用尖括号来实现的。在类或方法的声明中,可以使用泛型来指定一个或多个类型参数。 在类的声明中,可以在类名后面...
    99+
    2023-10-27
    java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作