当前位置:首页 > 科技  > 软件

Java 泛型编程所说的类型擦除到底是什么?

来源: 责编: 时间:2024-06-07 17:17:23 120观看
导读大部分语言都支持泛型,泛型是一种语言机制,各种语言的实现机制都不太一样,例如C++使用模板方式来实现泛型,而 Java 中用类型擦除机制来实现泛型。什么是泛型在 Java 中,不会泛型,寸步难行。泛型可能是一个 Java 初学者需要

大部分语言都支持泛型,泛型是一种语言机制,各种语言的实现机制都不太一样,例如C++使用模板方式来实现泛型,而 Java 中用类型擦除机制来实现泛型。u0928资讯网——每日最新资讯28at.com

什么是泛型

在 Java 中,不会泛型,寸步难行。泛型可能是一个 Java 初学者需要攻克的第一个难点。随便跟着一门教程或 任何一本《Java入门到精通》,前面关于变量、关键字、语法(if、while、for等等)这些基本上是一看就懂,而当内容来到泛型的时候,大部分人可能就突然感觉没那么轻松了。u0928资讯网——每日最新资讯28at.com

如果没有编程经验的话,可能需要练习一段时间才能完全掌握泛型编程概念和技巧,这么说吧,有些人写了好几年代码,碰到泛型的时候可能还是不太熟练。u0928资讯网——每日最新资讯28at.com

说到Java泛型,最明显的标志就是 <> 。u0928资讯网——每日最新资讯28at.com

泛型是什么呢?通俗的说就是一个类型是没有固定类型的,即可以是Integer 也可以是 Long,还可能是你自定义的类。u0928资讯网——每日最新资讯28at.com

泛型使类型(类和接口)能够在定义类、接口和方法时成为参数。与方法声明中使用的更熟悉的形式参数非常相似,类型参数为您提供了一种通过不同输入重复使用相同代码的方法。区别在于形式参数的输入是值,而类型参数的输入是类型。u0928资讯网——每日最新资讯28at.com

例如在类定义中使用泛型,最常见的 ArrayListu0928资讯网——每日最新资讯28at.com

public class ArrayList<E> extends AbstractList<E>        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{//... code}

例如在方法参数中使用泛型,来一个复杂的例子u0928资讯网——每日最新资讯28at.com

public static <T extends Number & Comparable<T>, U extends List<T>, R extends T> R complexMethod(U list, T element) {}

在这个例子中,有两个传入参数 U list, T element,而这两个参数需要在方法的返回类型前用<>做出说明,也就是 <T extends Number & Comparable<T>, U extends List<T>, R extends T>这一部分。u0928资讯网——每日最新资讯28at.com

返回值也是一个泛型 R。u0928资讯网——每日最新资讯28at.com

为什么是 T、U、R

经常看到泛型类型用 T、U、R,还有K、V 这样的符号表示。我们肯定知道不用T也完全没问题,用 X 也可以。u0928资讯网——每日最新资讯28at.com

之所以这么统一是因为这是官方比较推荐的写法,推荐的规则如下:u0928资讯网——每日最新资讯28at.com

  • E - 表示一个元素,例如集合元素、数组元素
  • K - 表示一个 Key,键值对经常用到,与之对应的是 V
  • V - 表示一个 Value,键值对经常用
  • N - 表示 Number(数字类型)
  • T - 这个见得最多,表示一个类型 Type,不管是基础类型还是自定义的类

泛型的作用

前面也说了,当一个参数预期可能有多种类型的时候,就会用到泛型,那既然是类型不确定,那直接用 Object 不就行了吗,何必费事儿呢?一会儿讲到类型擦除的时候会发现,本身类型擦除的核心就是把泛型类型转为 Object。但是这是编译器干的,为了给JVM看的。而作为开发者和编译器,使用泛型还是有很大好处的。u0928资讯网——每日最新资讯28at.com

1、在编译时提供更严格的类型检查,如果代码违反类型安全,编译器可以及时发现,而不是等到运行的时候抛出运行时异常。u0928资讯网——每日最新资讯28at.com

2、使程序员能够实现通用算法。通过使用泛型,程序员可以实现适用于不同类型集合的泛型算法,可以自定义,并且类型安全且更易于阅读。u0928资讯网——每日最新资讯28at.com

例如下面这个方法,只接受Number 类型的参数,用来比较两数的大小。u0928资讯网——每日最新资讯28at.com

public static <T extends Number> Boolean compare(T first, T second) {        double firstValue = first.doubleValue();        double secondValue = second.doubleValue();        return firstValue > secondValue;    }

3、消除不必要的类型转换。u0928资讯网——每日最新资讯28at.com

例如下面不用泛型的情况,每次取数据的时候都要转换一下类型。u0928资讯网——每日最新资讯28at.com

List list = new ArrayList();list.add("hello");String s = (String) list.get(0);

而用了泛型后,就不用自己转换了。u0928资讯网——每日最新资讯28at.com

List<String> list = new ArrayList<String>();list.add("hello");String s = list.get(0);

类型擦除

Java 中的泛型实现可以说就是用的类型擦除原理。通俗一点说,类型只在编译期存在,在运行时就不在了,都变为了 Object,一视同仁。u0928资讯网——每日最新资讯28at.com

在我们写好代码进行编译时,编译器会将泛型参数的类型进行替换,大部分情况下会将类型替换为 0bject 类型。这种行为模式用类型擦除来描述就非常形象。u0928资讯网——每日最新资讯28at.com

类型擦除原理

在类型擦除过程中,Java 编译器会擦除所有类型参数,如果类型参数有界,则用其第一个边界替换每个参数;如果类型参数无界,则用 Object 替换。u0928资讯网——每日最新资讯28at.com

在类型擦除过程中,编译器会按照以下规则来处理泛型类型参数:u0928资讯网——每日最新资讯28at.com

如果类型参数有界(bounded type),即使用了extends关键字限定了类型的上界,例如<T extends Number>,则编译器会用该类型的第一个边界来替换类型参数。u0928资讯网——每日最新资讯28at.com

例如下面这个例子,泛型 T 继承了Number类型,又实现了 Displayable 接口(没错,泛型可以这样定义)u0928资讯网——每日最新资讯28at.com

interface Displayable {    void display();}public class Result<T extends Number & Displayable> {    private T value;    public Result(T value) {        this.value = value;    }    public T getValue() {        return value;    }     public void show() {        value.display();    }}

在编译器进行类型擦除后会变成下面这样,因为 T 的上限是 Number,所以直接将 T 替换为 Number。u0928资讯网——每日最新资讯28at.com

public class Result {    private Number value;    public Result(Number value) {        this.value = value;    }    public Number getValue() {        return value;    }}

如果类型参数无界(unbounded type),即没有限定类型的上界,例如<T>,则编译器会用Object类型来替换类型参数。u0928资讯网——每日最新资讯28at.com

例如下面方法,没有指定类型上限类型。u0928资讯网——每日最新资讯28at.com

public static <T> int count(T[] anArray, T elem) {    int cnt = 0;    for (T e : anArray)        if (e.equals(elem))            ++cnt;        return cnt;}

经过编译器的擦除处理后,就变成下面这样,都替换成了 Object。u0928资讯网——每日最新资讯28at.com

public static int count(Object[] anArray, Object elem) {    int cnt = 0;    for (Object e : anArray)        if (e.equals(elem))            ++cnt;        return cnt;}

桥接方法

来看一下下面这段代码u0928资讯网——每日最新资讯28at.com

public class Node<T> {    public T data;    public Node(T data) { this.data = data; }    public void setData(T data) {        this.data = data;    }}public class SubNode extends Node<Integer> {    public SubNode(Integer data) { super(data); }    public void setData(Integer data) {        super.setData(data);    }     public static void main(String[] args) {        SubNode subNode = new SubNode(8);        Node node = subNode;        node.setData("Hello");        Integer x = subNode.data;    }}

这段代码大家一看就知道肯定是有问题的,运行的时候会出现 ClassCastException,但是编译是可以通过的。u0928资讯网——每日最新资讯28at.com

而运行时出现错误的代码是 node.setData("Hello");这一行,但是经过前面对类型擦除的了解,Node 类的 setData 参数肯定被擦除成了 Object 类型了,既然是 Object,那Integer 和 String 都满足啊,为啥还会报错呢。u0928资讯网——每日最新资讯28at.com

这就要说到桥接了。u0928资讯网——每日最新资讯28at.com

当编译器对泛型扩展的类或接口进行编译处理的时候,会根据实际的类型进行方法的桥接处理。什么意思呢,还是拿上面的 Node 和 SubNode 类说明。u0928资讯网——每日最新资讯28at.com

类型擦除后的代码是下面这样的,多了一个桥接方法。u0928资讯网——每日最新资讯28at.com

public class Node {    public Object data;    public Node(Object data) { this.data = data; }    public void setData(Object data) {        this.data = data;    }}public class SubNode extends Node {    public SubNode(Integer data) { super(data); } /** ** 桥接方法 **/ public void setData(Object data) {        setData((Integer) data);    }    public void setData(Integer data) {        super.setData(data);    }}

为什么需要这个桥接方法呢?u0928资讯网——每日最新资讯28at.com

Node 类的 setData 方法入参是 Object 类型。u0928资讯网——每日最新资讯28at.com

public void setData(Object data) {    this.data = data;}

而 SubNode 的setData 方法入参是 Integer。u0928资讯网——每日最新资讯28at.com

public void setData(Integer data) { super.setData(data);}

所以,SubNode 的 setData 方法并不会重写父类 Node 的setData 方法,而想要重写的话,就必须让 SubNode 的setData 的入参也是 Object,这就是桥接方法的由来。u0928资讯网——每日最新资讯28at.com

public void setData(Object data) { setData((Integer) data);}

这样一来重写父类的方法,但是要把参数强转成 Integer。u0928资讯网——每日最新资讯28at.com

前面说的 node.setData("Hello");这一行会报错,那大家就知道为什么了吧,是因为把 Hello强转为 Integer 的时候出现的错误。u0928资讯网——每日最新资讯28at.com

总结

正是类型擦除的机制帮助 Java 实现了泛型编程,让我们作为开发者能够更好的了解和控制我们正在使用类型的是什么,而不是 Object 满天飞。u0928资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-92736-0.htmlJava 泛型编程所说的类型擦除到底是什么?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Rathole:基于Rust开发的轻量级高性能反向代理,替代Frp和Ngrok!

下一篇: K9s:终端中的 Kubernetes 集群管理

标签:
  • 热门焦点
  • 官方承诺:K60至尊版将会首批升级MIUI 15

    官方承诺:K60至尊版将会首批升级MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版将会搭载天玑9200+处理器和独显芯片X7的同时,Redmi给出了官方承诺,K60至尊重大更新首批升级,会首批推送MIUI 15。也就是说虽然
  • 小米平板5 Pro 12.4简评:多专多能 兼顾影音娱乐的大屏利器

    小米平板5 Pro 12.4简评:多专多能 兼顾影音娱乐的大屏利器

    疫情带来了网课,网课盘活了安卓平板,安卓平板市场虽然中途停滞了几年,但好的一点就是停滞的这几年行业又有了新的发展方向,例如超窄边框、高刷新率、多摄镜头组合等,这就让安卓
  • 帅气纯真少年!日本最帅初中生选美冠军出炉

    帅气纯真少年!日本最帅初中生选美冠军出炉

    日本第一帅哥初一生选美大赛冠军现已正式出炉,冠军是来自千叶县的宗田悠良。日本一直热衷于各种选美大赛,从&ldquo;最美JK&rdquo;起到&ldquo;最美女星&r
  • JavaScript学习 -AES加密算法

    JavaScript学习 -AES加密算法

    引言在当今数字化时代,前端应用程序扮演着重要角色,用户的敏感数据经常在前端进行加密和解密操作。然而,这样的操作在网络传输和存储中可能会受到恶意攻击的威胁。为了确保数据
  • JVM优化:实战OutOfMemoryError异常

    JVM优化:实战OutOfMemoryError异常

    一、Java堆溢出堆内存中主要存放对象、数组等,只要不断地创建这些对象,并且保证 GC Roots 到对象之间有可达路径来避免垃 圾收集回收机制清除这些对象,当这些对象所占空间超过
  • 共享单车的故事讲到哪了?

    共享单车的故事讲到哪了?

    来源丨海克财经与共享充电宝相差不多,共享单车已很久没有被国内热点新闻关照到了。除了一再涨价和用户直呼用不起了。近日多家媒体再发报道称,成都、天津、郑州等地多个共享单
  • 本地生活这块肥肉,拼多多也想吃一口

    本地生活这块肥肉,拼多多也想吃一口

    出品/壹览商业 作者/李彦编辑/木鱼拼多多也看上本地生活这块蛋糕了。近期,拼多多在App首页&ldquo;充值中心&rdquo;入口上线了本机生活界面。壹览商业发现,该界面目前主要
  • 华为Mate60标准版细节曝光:经典星环相机模组回归

    华为Mate60标准版细节曝光:经典星环相机模组回归

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
  • 超级标准版旗舰!iQOO 11S全球首发iQOO超算独显芯片

    超级标准版旗舰!iQOO 11S全球首发iQOO超算独显芯片

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
Top
Baidu
map