Java泛型-类型擦除

Java泛型编程如何避免类型擦除问题 类型擦除是指在编译时,泛型类型信息被擦除,替换为它们的原始类型(如Object)。理解类型擦除的原理和影响,以及掌握如何在编程中处理这些问题,将有助于我们更好地利用Java泛型编程的优势,提高代码的质量和可维护性。因此,在运行时,泛型类型信息不再存在,导致无法直接访问这些类型。例如,我们可以为泛型类添加一个Class类型的类型参数,以便在运行时访问泛型类型。通过利用这些特性,我们可以减少显式类型参数的使用,从而减轻类型擦除带来的负担。通过这种方式,我们可以在运行时访问泛型类型,从而避免一些类型擦除问题。 阅读详情

一、概述

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

二、编译器如何处理泛型?

     通常情况下,一个编译器处理泛型有两种方式:
     1.Code specialization。在实例化一个泛型类或泛型方法时都产生一份新的目标代码(字节码or二进制代码)。例如,针对一个泛型list,可能需要 针对string,integer,float产生三份目标代码。
     2.Code sharing。对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。
     C++中的模板(template)是典型的Code specialization实现。C++编译器会为每一个泛型类实例生成一份执行代码。执行代码中integer list和string list是两种不同的类型。这样会导致代码膨胀(code bloat),不过有经验的C++程序员可以有技巧的避免代码膨胀。
     Code specialization另外一个弊端是在引用类型系统中,浪费空间,因为引用类型集合中元素本质上都是一个指针。没必要为每个类型都产生一份执行代码。而这也是Java编译器中采用Code sharing方式处理泛型的主要原因。
     Java编译器通过Code sharing方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

三、 什么是类型擦除?

     类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。
     类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
     类型擦除的主要过程如下:
     1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
     2.移除所有的类型参数。
     如

Java代码  收藏代码
  1. interface Comparable <A> {   
  2.   public int compareTo( A that);   
  3. }   
  4. final class NumericValue implements Comparable <NumericValue> {   
  5.   priva te byte value;    
  6.   public  NumericValue (byte value) { this.value = value; }    
  7.   public  byte getValue() { return value; }    
  8.   public  int compareTo( NumericValue t hat) { return this.value - that.value; }   
  9. }   
  10. -----------------  
  11. class Collections {    
  12.   public static <A extends Comparable<A>>A max(Collection <A> xs) {   
  13.     Iterator <A> xi = xs.iterator();   
  14.     A w = xi.next();   
  15.     while (xi.hasNext()) {   
  16.       A x = xi.next();   
  17.       if (w.compareTo(x) < 0) w = x;   
  18.     }   
  19.     return w;   
  20.   }   
  21. }   
  22. final class Test {   
  23.   public static void main (String[ ] args) {   
  24.     LinkedList <NumericValue> numberList = new LinkedList <NumericValue> ();   
  25.     numberList .add(new NumericValue((byte)0));    
  26.     numberList .add(new NumericValue((byte)1));    
  27.     NumericValue y = Collections.max( numberList );    
  28.   }   
  29. }<span style="color: #333333; font-family: Arial; font-size: 14px;">  
  30. </span>  

经过类型擦除后的类型为
 interface Comparable { 

Java代码  收藏代码
  1.   public int compareTo( Object that);   
  2. }   
  3. final class NumericValue implements Comparable {   
  4.   priva te byte value;    
  5.   public  NumericValue (byte value) { this.value = value; }    
  6.   public  byte getValue() { return value; }    
  7.   public  int compareTo( NumericValue t hat)   { return this.value - that.value; }   
  8.   public  int compareTo(Object that) { return this.compareTo((NumericValue)that);  }   
  9. }   
  10. -------------  
  11. class Collections {    
  12.   public static Comparable max(Collection xs) {   
  13.     Iterator xi = xs.iterator();   
  14.     Comparable w = (Comparable) xi.next();   
  15.     while (xi.hasNext()) {   
  16.       Comparable x = (Comparable) xi.next();   
  17.       if (w.compareTo(x) < 0) w = x;   
  18.     }   
  19.     return w;   
  20.   }   
  21. }   
  22. final class Test {   
  23.   public static void main (String[ ] args) {   
  24.     LinkedList numberList = new LinkedList();   
  25.     numberList .add(new NumericValue((byte)0));  ,  
  26.     numberList .add(new NumericValue((byte)1));    
  27.     NumericValue y = (NumericValue) Collections.max( numberList );    
  28.   }   
  29. }<span style="color: #333333; font-family: Arial; font-size: 14px;">  
  30. </span>  

第一个泛型类Comparable <A>擦除后 A被替换为最左边界Object。Comparable<NumericValue>的类型参数NumericValue被擦除掉,但是这直 接导致NumericValue没有实现接口Comparable的compareTo(Object that)方法,于是编译器充当好人,添加了一个桥接方法。
第二个示例中限定了类型参数的边界<A extends Comparable<A>>A,A必须为Comparable<A>的子类,按照类型擦除的过程,先讲所有的类型参数 ti换为最左边界Comparable<A>,然后去掉参数类型A,得到最终的擦除后结果。

四、类型擦除带来的问题

     正是由于类型擦除的隐蔽存在,直接导致了众多的泛型灵异问题。
 Q1.用同一泛型类的实例区分方法签名?——NO!
    import java.util.*;

Java代码  收藏代码
  1.     public class Erasure{  
  2.   
  3.             public void test(List<String> ls){  
  4.                 System.out.println("Sting");  
  5.             }  
  6.             public void test(List<Integer> li){  
  7.                 System.out.println("Integer");  
  8.             }  
  9.     }<span style="color: #333333; font-family: Arial; font-size: 14px;">  
  10. </span>  

编译该类,

 

参数类型明明不一样啊,一个List<String>,一个是List<Integer>,但是,偷偷的说,type erasure之后,它就都是List了⋯⋯
Q2. 同时catch同一个泛型异常类的多个实例?——NO!
同理,如果定义了一个泛型一场类GenericException<T>,千万别同时catch GenericException<Integer>和GenericException<String>,因为他们是一样一样滴⋯⋯
Q3.泛型类的静态变量是共享的?——Yes!
猜猜这段代码的输出是什么?

Java代码  收藏代码
  1. import java.util.*;  
  2.   
  3. public class StaticTest{  
  4.     public static void main(String[] args){  
  5.         GT<Integer> gti = new GT<Integer>();  
  6.         gti.var=1;  
  7.         GT<String> gts = new GT<String>();  
  8.         gts.var=2;  
  9.         System.out.println(gti.var);  
  10.     }  
  11. }  
  12. class GT<T>{  
  13.     public static int var=0;  
  14.     public void nothing(T x){}  
  15. }<span style="color: #333333; font-family: Arial; font-size: 14px;">  
  16. </span>  

答案是——2!由于经过类型擦除,所有的泛型类实例都关联到同一份字节码上,泛型类的所有静态变量是共享的。

五、Just remember

1.虚拟机中没有泛型,只有普通类和普通方法
2.所有泛型类的类型参数在编译时都会被擦除
3.创建泛型对象时请指明类型,让编译器尽早的做参数检查(Effective Java,第23条:请不要在新代码中使用原生态类型)
4.不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。

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

相关推荐

Java泛型原理篇: 类型擦除以及桥接方法

文章开始之前首先还是先思考一个经典问题 List<String> stringList = new ArrayList<>(); List<Intege.

笔墨的专栏 3734

Java 类型擦除

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

xiangjingjing 360

Java中的泛型与类型擦除

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

shejizuopin的博客 837

泛型之类型擦除

泛型之类型擦除         泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。        通俗地讲,泛型类和普通类在java虚拟机内没有什么不同。 实例: List&lt;String&gt; 1=new ArrayList&lt;String&gt;(); List&lt;Integer&gt; 2=new ArrayList&l...

weixin_42560233的博客 220

java 泛型原理 类型擦除

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

sowhat 3万+

Java泛型:类型擦除

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

流楚丶格念的博客 3876

Java 泛型:深入理解泛型的类型擦除

泛型代码和虚拟机 Java 中的的泛型是伪泛型,为什这么说呢?因为 Java 在编译期间,所有的泛型信息都被擦除掉。称为类型擦除(type erasure) 在 Java 虚拟机中没有泛型类型对象 – 所有对象都是属于普通类 , 所以我们要了解一下 类型擦除 的概念。 1. 类型擦除 无论何时定义一个泛型类型,都会自动提供一个响应的 原始类型 (raw type)(不存在泛型 )。原始类型的名字...

Strange_Monkey的博客 3162

[南大ICS-PA2] 字符串处理函数和printf实现

[南大ICS-PA2] 程序、运行时环境与AM 字符串处理函数和printf实现

qq_42474104的博客 2985

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

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

frank 的专栏 10万+

HDMI调试避坑指南:从AVI InfoFrame到通用控制包,这些DataIsland Packet细节千万别忽略

本文深入解析HDMI调试中DataIsland数据包的关键细节,包括AVI InfoFrame、通用控制包和音频时钟再生包(ACR)的配置与故障排查。通过实际案例和寄存器配置示例,帮助工程师快速定位显示异常、色彩失真和音频中断等问题,提升HDMI接口调试效率。

weixin_30897233的博客 635

数值计算之 插值法(4)切比雪夫零点插值

数值计算之 插值法(4)切比雪夫零点插值前言插值点选取第一类切比雪夫多项式拉格朗日插值多项式的余项切比雪夫零点插值后记 前言 上篇插值法讨论了多项式插值的解,以及龙格现象。本篇将介绍一种在抽取节点时有效降低龙格现象的方法——切比雪夫零点插值。 插值点选取 插值多项式阶数较高时,在取值空间均匀取点,容易出现龙格现象。 即区间边缘的插值结果与原函数差异很大,而区间中央的插值结果相对较好。这表明,高阶多项式插值对区间中央的节点拟合好,而对两端节点拟合效果差。 自然而然会想到,在两端多采样一些节点,在中间少采样一

qq_41035283的博客 1万+

Java 泛型的本质——类型擦除

泛型总共干了4件事: 编译之前编译器进行的静态分析检查(比如你不能把ArrayList<Integer>里添加string,如果做了无法编译)。 强制转换类型(比如你从ArrayList<Integer>里取一个元素时,编译器隐式地在字节码里添加一句强制转换类型,转换为int)。 在字节码中,用类型参数的限定(如果没有就是Object)来替换所有类型参数(保留上界)。这就是类型擦除。 在继承使用了类型参数的方法时(形参或者返回值的类型是T),用桥方法来保持多态。

anlian523的博客 4657

数据中心机房建设标准及依据

深刻理解、尊重并恪守这些依据,是我们以科学、经济、可靠的方式,为蓬勃发展的数字经济筑牢基石的唯一路径。让我们共同努力,以标准为依据,以匠心为精神,建造更多世界一流的高效能、高可用、高智能的绿色数据中心,共同赋能数字中国的宏伟事业,支撑中华民族在数字时代的伟大复兴征程中稳步前行!今天我将力图以宏大的视角和深入的剖析,系统性地阐述数据中心机房建设所应遵循的多层次、多维度的标准依据,揭示这些依据背后的深层逻辑与时代使命,并探讨其如何共同作用于一座世界级数据中心的诞生。,以支持万兆乃至更高速率的传输。

qq_39980997的博客 1137

Java 泛型——类型擦除

import java.util.*; public class t7 { public static void main(String[] args) { // TODO Auto-generated method stub List&lt;String&gt; ll = new ArrayList&lt;&gt;(); List&lt;Integer&gt; kk = ne...

Hern(宋兆恒) 9457

VASPKIT绘制平面平均电荷差分密度

平面平均密度差(planar-averaged electron density difference)是密度差的衍生。密度差是个三维空间函数,经常通过绘制平面图、等值面图方式图形化考察。虽然这两种图很直观,但平面图会受到截面选取的影响,而等值面图会受到等值面数值(isovalue)选取的影响,因此都有一定任意性,也不容易定量讨论。对于电子转移有比较明确方向的情况,可以绘制平面平均密度差曲线,由此可以清楚地考察垂直于选取的方向上每个截面上的密度差积分值,在定量讨论、对比上比较便利。

weixin_43948934的博客 1万+

Mamba-YOLO训练前必看:从环境配置到数据集调整的完整流程

本文详细介绍了Mamba-YOLO目标检测框架的完整训练流程,包括环境配置、数据集优化和模型调参。从硬件准备到依赖安装,再到数据增强和超参数调整,提供了一系列实用技巧和常见问题解决方案,帮助开发者高效训练Mamba-YOLO模型,避免常见踩坑记录。

tech5的博客 826

Java中泛型——类型擦除

在程序中可以包含不同类型的Pair, 如Pair擦除类型后他们就成了原始的Pair 类型,原始类型都是Object。泛型是java1.5版本才引进的概念,在这之前没有泛型,但是,泛型代码能够很好的和之前版本的代码兼容,原因是泛型只存在代码编译阶段,在进入JVM 之前,泛型相关的信息会被擦除掉,我们称之为类型擦除。原始类型就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。...

Java_ttcd的博客 5648

matsim教程及其案例

MATSim 是一个强大的交通仿真工具,适合研究交通政策、出行行为优化和城市规划。建议从官方示例入手,逐步尝试修改参数和扩展功能。遇到问题时,优先查阅官方文档和社区讨论。(Multi-Agent Transport Simulation Toolkit)的教程和案例资源整理,涵盖基础入门、核心概念、实际应用案例及学习路径。MATSim 是一个开源的、基于多智能体的交通仿真框架,用于模拟城市交通系统中的出行行为、交通流量和交通政策影响。

weixin_70682362的博客 1092

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

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

yaoxin521123的博客 1171

【知识点讲解】大模型参数量与计算量(FLOPs)估算方法

指标参数量 (Params)计算量 (FLOPs)含义模型的大小,静态模型的计算复杂度,动态决定因素模型结构(宽度、深度)模型结构 +输入数据(B, S)影响内存占用、磁盘空间运行速度、能耗单位M (百万), B (十亿)工具thopptflopstorchinfothopptflopsfvcore重要提示FLOPs ≠ 速度:FLOPs 是理论计算量。实际速度还受内存带宽 (Memory Bandwidth)并行度硬件架构等因素严重影响。

AI浩 2095

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

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

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Java泛型(类型擦除)

Java泛型类型擦除

weixin_44298385的博客 3278

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Java 泛型与类型擦除

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

专注Java和MySQL领域知识分享 1270
上一篇: JVM client模式和Server模式的区别
下一篇: Java泛型与类型擦除--ImportNew
java85140031
博客等级 码龄18年 34粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值