Java中泛型——类型擦除

深入解析Java泛型与类型擦除机制 本文深入探讨了Java泛型编程中的核心概念——类型擦除。通过解释类型擦除的原因、影响以及如何应对,我们揭示了泛型代码在运行时的实际行为。同时,文章还介绍了通配符的使用,以及如何利用相关技巧来规避类型擦除带来的挑战。理解类型擦除对于确保泛型代码的安全性和性能至关重要。 阅读详情

问题导引

这个例子里,定义了两个List集合,不过一个是List泛型类型的,只能存储整数;一个 是List泛型类型的,

只能存储字符串。最后我们通过list1对象和list2对象的 getClass()方法获取他们的类的信息,结果发现结

果为true。说明泛型类型String和 Integer都被擦除掉了,只剩下原始类型。

例如如下代码:

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package com.fanxing;

import java.util.ArrayList;
import java.util.List;

public class  FanTest  { 
    public static void main(String[] args) { 
        List<Integer> list1 = new ArrayList<Integer>();
        List<String> list2 = new ArrayList<String>();
        System.out.println(list1.getClass() == list2.getClass());
    }
}

他的运行结果是true:

这就是类型擦除,下面咱们来详细查看一下:

类型擦除概念

泛型是java1.5版本才引进的概念,在这之前没有泛型,但是,泛型代码能够很好的和之前版本的代码兼容,原因是泛型只存在代码编译阶段,在进入JVM 之前,泛型相关的信息会被擦除掉,我们称之为类型擦除

类型擦除应用场景

需要值得注意的一点是:类型擦除后保留的原始类型

原始类型就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

看下面两个例子理解一下:

案例:原始类型被Object替换

代码如下(不能运行,就是看一下这个特性)

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Pair<T>  { 
    //原始类型就是Object
    private T value;

    public  T  getValue()  { 
        return value;
    }

    public  void  setValue(T value)  { 
        this.value = value;
    }

    public  static  void  main(String[] args)  { 
        Pair pair = new Pair();
        pair.setValue();//调用setter方法能看到参数类型是 Object:setValue(Object) ctrl+鼠标放到setValue()中能看到setValue(Object)
    }
}

因为在Pair中,T是一个无限定的类型变量,所以用Object代替,其结果就是一个普通的类,如同泛型加入Java之前就已经实现了。在程序中可以包含不同类型的Pair, 如Pair擦除类型后他们就成了原始的Pair 类型,原始类型都是Object。

案例:原始类型被限定类型替换

我们可以用extends设置上届

<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  Pair<T  extends  Comparator>  { 
    //原始类型就是Comparator
    private T value;

    public T getValue() { 
        return value;
    }

    public void setValue(T value) { 
        this.value = value;
    }

    public static void main(String[] args) { 
        Pair pair = new Pair();
        pair.setValue();//调用setter方法能看到参数类型是 Comparator:setValue(Comparator)
    }
}

这里相应的原始类型都会被自动提供了,原始类型就用第一个边界的类型替换,也就是Comparator

类型擦除的限制

无法利用同一泛型类的实例区分方法签名

例如下面代码,就会报错:

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class Erasure { 
    //虽然泛型实例不同,但是由于类型擦除后是同一字节码(类型擦拭之后,就都是 List了),因此不能 用于区分方法签名
    public  void  test(List<String> list)  { 
        System.out.println("String");
    }

    public  void  test(List<Integer> list)  { 
        System.out.println("Integer");
    }
}

虽然泛型实例不同,但是由于类型擦除后是同一字节码(类型擦拭之后,就都是 List了),因此不能 用于区分方法签名 +

泛型类的静态变量是共享的

例如下面代码:

<pre class="prettyprint hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class  StaticTest  { 
    public static void main(String[] args) { 
        System.out.println(GT.var);
        GT<Integer> gti = new GT<>();
        gti.var = 1;
        System.out.println(GT.var);
        GT<String> gts = new GT<>();
        gts.var = 2;//泛型参数不同,但是共享类的静态成员
        System.out.println(GT.var);
    }
}

class GT<T>  { 
    public static int var = 0;

    public void nothing(T x) { 
    }
}

结果如下所示,泛型参数不同,但是共享类的静态成员:

类型擦除的特征

  • 所有泛型类的类型参数在编译时都会被擦除,虚拟机运行时中没有泛型,只有普通类和普通方法,从这一点上来说,Java中的泛型从某种程度上是一种语法糖

  • Java泛型不支持基本类型 例如: short int double等

  • 在泛型代码内部,无法获得任何有关泛型参数类型的信息,如果传入的类型参数为T,那么在泛型代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了

  • 创建泛型对象时请指明类型,让编译器尽早的做参数检查

  • 不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你

  • Java的泛型类型不能用于new构建对象(也不能用于初始化数组).泛型不能用于显性地引用运行时类型的操作之中,例如转型,instanceof和new操作(包括new一个对象,new一个数组),因为所有关于参数的类型信息都在运行时丢失了,所以任何在运行时需要获取类型信息的操作都无法进行工作。

Java泛型原理篇: 类型擦除以及桥接方法 文章开始之前首先还是先思考一个经典问题 List<String> stringList = new ArrayList<>(); List<Intege. 阅读详情

相关推荐

Java中的泛型与类型擦除

特性优点缺点泛型类型安全、代码重用、简化代码类型擦除导致运行时信息丢失、某些操作受限类型擦除兼容性(与旧版Java代码兼容)、实现简单运行时无法获取泛型类型信息、灵活性受限Java泛型与类型擦除是Java语言中的重要特性,它们为开发者提供了类型安全和代码重用的能力,但同时也带来了一些限制和挑战。通过深入理解泛型与类型擦除的工作原理,并掌握实用的解决技巧,我们可以更好地利用Java泛型来编写高效、安全的代码。

shejizuopin的博客 837

【Java】 泛型擦除

1. 泛型擦除的介绍 1.1 泛型擦除的原因 1.2 泛型擦除规则 1.3 泛型擦除规则的验证 2. 通过反射获取被擦除的泛型信息 3. 使用匿名内部类解决泛型擦除导致的问题 4. 泛型擦除在Flink中的坑 4.1 算子实参有时用Lambda表达式代替匿名内部类会报错 4.2 new OutputTag(){}

渐渐的忘记,赶不上明天(读研ing,2024年毕业) 1万+

深入学习 Java 泛型实现方式:擦除法!

在 Java 中,泛型是一个非常重要的概念,它使得代码能够更加灵活和类型安全。你可能已经在开发中使用过泛型,它允许你在编译时指定类型,而在运行时保证类型安全。但你是否了解过 Java 泛型的实现方式?很多人可能并不知道,Java 泛型的实现是通过一种叫做“擦除法”(Type Erasure)的方法实现的。今天,我们将深入探索 Java 泛型是如何实现的,特别是“擦除法”背后的原理和实际应用。通过这篇文章,你将更加清楚地理解泛型在 Java 中的工作原理,以及为什么 Java 需要使用擦除法来实现泛型。

喵手的博客 867

什么是类型擦除?

它指的是在编译时,Java编译器会将泛型类型参数(如T、E等)替换为其限定类型(bounded type)或者Object类型,并在必要的地方插入类型转换代码。这样,旧的代码可以在不做任何修改的情况下继续工作,而新的泛型代码可以在编译时提供类型安全。否则,替换为Object类型。由于类型擦除后,泛型类型信息在运行时不再可用,因此编译器需要在合适的地方插入类型转换代码,以确保类型安全。例如,对于一个泛型类List<T>,编译器会将其转换为一个非泛型类List,其中所有的T类型参数被替换为Object。

weixin_72084017的博客 1827

java 泛型原理 类型擦除

1.Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。 如在代码中定义List<Object>和L...

sowhat 3万+

Java泛型:类型擦除

Java的泛型类型不能用于new构建对象(也不能用于初始化数组).泛型不能用于显性地引用运行时类型的操作之中,例如转型,instanceof和new操作(包括new一个对象,new一个数组),因为所有关于参数的类型信息都在运行时丢失了,所以任何在运行时需要获取类型信息的操作都无法进行工作。原始类型就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。一个 是List泛型类型的,...

流楚丶格念的博客 3876

Java 泛型,你了解类型擦除吗?

泛型,一个孤独的守门者。 大家可能会有疑问,我为什么叫做泛型是一个守门者。这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇。泛型是 Java 中一个很小巧的概念,但同时也是一个很容易让人迷惑的知识点,它让人迷惑的地方在于它的许多表现有点违反直觉。文章开始的地方,先给大家奉上一道经典的测试题。List&lt;String&gt; l1 = new ArrayList&lt;Stri

frank 的专栏 10万+

Java泛型和类型擦除

public class 类名 {// 注意:静态方法和静态变量不能使用泛型类所声明的泛型类型}}}}}public interface 接口名 {}}}@Override}}@Override}}修饰符 返回类型 方法名(参数) {}return t;}

生活在别处 1136

Java泛型-类型擦除

Java泛型-类型擦除一、概述      Java泛型在使用过程有诸多的问题,如不存在List.class, List不能赋值给List(不可协变),奇怪的ClassCastException等。 正确的使用Java泛型需要深入的了解Java的一些概念,如协变,桥接方法,以及这篇笔记记录的类型擦除。Java泛型的处理几乎都在编译器中进行,编译器生成的bytecode是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。二、编译器如何处理泛型?     通常情况下,一个编译器处理泛型有两种

caihaijiang的专栏 4万+

深入解析MOS管电容特性:从基础理论到高频应用

本文深入解析了MOS管的电容特性,从基础物理结构出发,详细拆解了栅氧电容、重叠电容和结电容的成因与影响。文章重点探讨了电容值随工作状态变化的非线性特性,并分析了其在CMOS高频开关电源、射频电路等应用中的关键作用,包括开关损耗、米勒效应、信号完整性挑战以及如何将电容特性转化为可控元件(如MOS变容管)的机遇。最后提供了从选型、仿真到PCB布局的实战设计指南。

weixin_29278805的博客 381

Palabos User Guide中文解读 | 第一章 | 介绍

译者的话:本人在学习palabos时,发现国内中文资料甚少,恰好网上可以直接搜到palabos user guide这种英文资料,加之时间充裕,便打算开始翻译,能不能完成全本翻译再说,而且文中我也会标记我不确定的地方,以传达意思为主,翻译不当的地方还请各位读者帮忙指正。 Palabos User Guide Release 1.0 r1 Copyright © 2019 University of...

Yulan Fang's notes. 6977

140. Java 泛型 - Java 泛型类型的擦除

摘要: Java 泛型通过类型擦除实现,在编译时移除泛型信息以确保向后兼容性。无界类型参数(如 T)擦除为 Object,有界参数(如 T extends Comparable)擦除为上界类型。类型擦除导致运行时无法获取泛型信息、禁止创建泛型数组,并通过桥接方法维持多态性。例如,List<String> 和 List<Integer> 运行时均为 List,而子类覆盖泛型方法时会生成桥接方法(如 Object getValue() 调用 String getValue())。理解类型

yaoxin521123的博客 1171

【分享】ArcGIS实现分子分母等标注实用技巧

前言 标注是显示在地图上的文字信息,是出图中不可或缺的重要元素,为了标注的样式丰富,摆放的位置灵活,因此给制图出图带来不少难度,以下分享几种本人在ArcGIS中的标注方法,实现上下标注、换行标注或分式标注的实用技巧。 普通模式与高级模式 默认为普通模式,勾选高级开启高级模式。 普通二分式标注(普通模式) [DLBM] & vbnewline & [DLMC] 标注效果展示 分行二分式标注(普通模式) [DLBM]& vbnewline & "——————" &

杨港 3万+

Java泛型类型擦除以及类型擦除带来的问题

Java泛型类型擦除以及类型擦除带来的问题1.Java泛型的实现方法:类型擦除1-2.通过两个例子证明Java类型的类型擦除2.类型擦除后保留的原始类型3.类型擦除引起的问题及解决方法3-1.先检查,再编译以及编译的对象和引用传递问题3-2.自动类型转换3-3.类型擦除与多态的冲突和解决方法3-4.泛型类型变量不能是基本数据类型3-5.编译时集合的instanceof3-6.泛型在静态方法和静态类中的问题 1.Java泛型的实现方法:类型擦除 1-2.通过两个例子证明Java类型的类型擦除 例1.原始类型相

LMR的博客 1841

昇腾AI原生创新算子挑战赛(S1赛季)复盘-LessEqual算子

【代码】昇腾AI原生创新算子挑战赛(S1赛季)复盘-LessEqual算子。

william_myq的博客 507

基于深度学习的X光安检危险品检测系统(YOLOv8 + UI界面 + 数据集)

随着全球安全形势的变化,机场、车站、地铁等场所的安检工作日益重要。X光安检系统已经成为现代公共安全领域中不可或缺的部分,能够有效地检测物品的内部结构,识别潜在的危险品。然而,传统的X光图像分析方法仍然依赖人工操作,检测效率较低,并且易受到人为因素的影响。近年来,深度学习技术的应用在图像分析领域取得了显著的突破,特别是在目标检测任务中,YOLO(You Only Look Once)模型因其高效性和精度,成为了许多目标检测任务的首选模型。

YOLO 1058

Java泛型(类型擦除)

Java泛型类型擦除

weixin_44298385的博客 3278

fanuc机器人KAREL中 函数的应用

本次课程讲解编程的思路。很多的同学都玩过积木,一个一个的模块逐渐累积起来,通过彼此之间的相互联系,最终形成一个完整的整体。其实编程也是这样。 将一个对象根据其特点进行模块化拆分,尤其是一些特点非常相似的内容,将其变成一个模块。然后通过传递参数,实现不同的功能,这就是模块化编程。

haier123888的博客 2743

Java 泛型与类型擦除

本文探讨了为什么 Java 不能实现真正意义上的泛型,并解释类型擦除的原理及其背后的原因。

专注Java和MySQL领域知识分享 1270

告别第三方控件!C#实现OPC UA客户端,对接工业设备超简单

C#实现OPC UA客户端无需依赖第三方商业控件,通过快速起步:通过NuGet安装,使用模拟器完成前期开发测试;复用性强:封装通用工具类,支持连接、读写、订阅等核心功能,可直接集成到各类上位机项目;界面友好:通过WinForm绑定工具类,实现可视化操作,适配工业现场人员使用;工业适配:支持安全配置、身份验证,联调步骤清晰,可快速对接西门子、欧姆龙等主流工业设备;稳定性高:具备资源释放、异常处理、线程安全UI更新等特性,满足工业7×24小时运行要求。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1360

Android性能分析——GPU资源耗尽分析(二十六)

本文分析了Android车机系统中GPU资源耗尽导致应用闪退的案例。通过日志定位到EGL_BAD_ALLOC错误,表明系统无法分配足够图形资源创建EGL上下文。问题主要发生在渲染线程初始化EGL环境时,根源在于车机系统对GPU资源的严格限制(如EGLContext数量、GPU内存等)。文中详细解读了SkiaOpenGLPipeline和EglManager的源码调用链路,指出createContext()函数中的致命断言是崩溃的直接原因,并提出了移除断言、增加重试机制、优雅降级等改进方案。该案例揭示了资源受

小旭的专栏 675

Java泛型类型擦除

文章目录1. 什么是类型擦除2. 什么是原始类型3. 类型擦除带来的问题和解决方法3.1 和多态的冲突3.2 不能使用`instanceof`3.3 在静态类和静态方法中的问题 1. 什么是类型擦除 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 Java的泛型是伪泛型,Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包

逍遥大俠 1603

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

141. Java 泛型 - Java 泛型方法的类型擦除

摘要: Java泛型方法通过类型擦除实现,编译时泛型参数会被替换为Object或上界类型。无界泛型方法(<T>)擦除为Object,有界泛型方法(<T extends X>)擦除为上界类型X。类型擦除导致运行时无法获取泛型具体类型,且不能直接创建泛型数组(需用Array.newInstance)。有界泛型可减少类型安全问题,但需注意方法调用时的强制转换。理解擦除机制有助于编写更安全的泛型代码。

yaoxin521123的博客 856

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Java中泛型是类型擦除的

Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉,看下面一个列子,代码如下: public class Foo { public void listMethod(List stringList){ } public void list

HUI_LANG的专栏 6125

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

上一篇: Java并发之ThreadLocal
下一篇: Java动态编程之Instrumentation
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值