JVM如何理解Java泛型类

激光三角测量法:斜射与直射式技术的原理与应用解析 本文深入解析激光三角测量法中斜射式与直射式技术的原理、应用及对比。斜射式适用于高精度小范围测量,尤其擅长处理反光表面;直射式则适合大范围位移检测,结构更简单。文章详细介绍了两种技术的光路分析、数学建模、应用场景及选型建议,并探讨了系统校准与误差分析方法,为工业检测领域提供实用参考。 阅读详情



Java代码  收藏代码
  1. //泛型代码  
  2. public class Pair<T>{  
  3.        private T first=null;  
  4.        private T second=null;  
  5.   
  6.        public Pair(T fir,T sec){  
  7.             this.first=fir;  
  8.         this.second=sec;  
  9.        }  
  10.        public T getFirst(){  
  11.              return this.first;  
  12.        }  
  13.        public T getSecond(){  
  14.              return this.second;  
  15.        }  
  16.        public void setFirst(T fir){  
  17.          this.first=fir;  
  18.        }  
  19. }  

 上面是一个很典型的泛型(generic)代码。T是类型变量,可以是任何引用类型。

 

1、Generic class 创建对象 
            Pair<String> pair1=new Pair("string",1);           ...①
            Pair<String> pair2=new Pair<String>("string",1)    ...②
      有个很有趣的现象:  ①代码在编译期不会出错,②代码在编译期会检查出错误。 
      这个问题其实很简单
      (1) JVM本身并没有泛型对象这样的一个特殊概念。所有的泛型类对象在编译器会全部变成普通类对象(这一点会在下面详细阐述)。
      比如①,②两个代码编译器全部调用的是 Pair(Object fir, Object sec)这样的构造器。
      因此代码①中的new Pair("string",1)在编译器是没有问题的,毕竟编译器并不知道你创建的Pair类型中具体是哪一个类型变量T,而且编译器肯定了String对象和Integer对象都属于Object类型的。
       但是一段运行pair1.getSecond()就会抛出ClassCastException异常。这是因为JVM会根据第一个参数"string"推算出T类型变量是String类型,这样getSecond也应该是返回String类型,然后编译器已经默认了second的操作数是一个值为1的Integer类型。当然就不符合JVM的运行要求了,不终止程序才怪。
       (2) 但代码②会在编译器报错,是因为new Pair<String>("string",1)已经指明了创建对象pair2的类型变量T应该是String的。所以在编译期编译器就知道错误出在第二个参数Integer了。
       小结一下: 
       创建泛型对象的时候,一定要指出类型变量T的具体类型。争取让编译器检查出错误,而不是留给JVM运行的时候抛出异常。

 

2、JVM如何理解泛型概念 —— 类型擦除 
    事实上,JVM并不知道泛型,所有的泛型在编译阶段就已经被处理成了普通类和方法。 
    处理方法很简单,我们叫做类型变量T的擦除(erased) 。
    无论我们如何定义一个泛型类型,相应的都会有一个原始类型被自动提供。原始类型的名字就是擦除类型参数的泛型类型的名字。
         如果泛型类型的类型变量没有限定(<T>) ,那么我们就用Object作为原始类型;
         如果有限定(<T extends XClass>),我们就XClass作为原始类型;
         如果有多个限定(<T extends XClass1&XClass2>),我们就用第一个边界的类型变量XClass1类作为原始类型;
 
    比如上面的Pair<T>例子,编译器会把它当成被Object原始类型替代的普通类来替代。

Java代码  收藏代码
  1. //编译阶段:类型变量的擦除  
  2. ublic class Pair{  
  3.        private Object first=null;  
  4.        private Object second=null;  
  5.   
  6.        public Pair(Object fir,Object sec){  
  7.            this.first=fir;  
  8.     this.second=sec;  
  9.        }  
  10.       public Object getFirst(){  
  11.             return this.first;  
  12.       }  
  13.       public void setFirst(Object fir){  
  14.      this.first=fir;  
  15.       }  
  16.    }  

 

3、泛型约束和局限性—— 类型擦除所带来的麻烦

(1)  继承泛型类型的多态麻烦。(—— 子类没有覆盖住父类的方法 )

     

     看看下面这个类SonPair

Java代码  收藏代码
  1. class SonPair extends Pair<String>{  
  2.           public void setFirst(String fir){....}  
  3. }  

     很明显,程序员的本意是想在SonPair类中覆盖父类Pair<String>的setFirst(T fir)这个方法。但事实上,SonPair中的setFirst(String fir)方法根本没有覆盖住Pair<String>中的这个方法。 
     原因很简单,Pair<String>在编译阶段已经被类型擦除为Pair了,它的setFirst方法变成了setFirst(Object fir)。那么SonPair中 setFirst(String)当然无法覆盖住父类的setFirst(Object)了。

     这对于多态来说确实是个不小的麻烦,我们看看编译器是如何解决这个问题的。

     编译器 会自动在 SonPair中生成一个桥方法(bridge method ) : 
           public void setFirst(Object fir){
                   setFirst((String) fir)
            }
 
      这样,SonPair的桥方法确实能够覆盖泛型父类的setFirst(Object) 了。而且桥方法内部其实调用的是子类字节setFirst(String)方法。对于多态来说就没问题了。

      问题还没有完,多态中的方法覆盖是可以了,但是桥方法却带来了一个疑问:

      现在,假设 我们还想在 SonPair 中覆盖getFirst()方法呢?

Java代码  收藏代码
  1. class SonPair extends Pair<String>{  
  2.       public String getFirst(){....}  
  3. }  

      由于需要桥方法来覆盖父类中的getFirst,编译器会自动在SonPair中生成一个 public Object getFirst()桥方法。 
      但是,疑问来了,SonPair中出现了两个方法签名一样的方法(只是返回类型不同):

            ①String getFirst()   // 自己定义的方法

            ②Object getFirst()  //  编译器生成的桥方法 
      难道,编译器允许出现方法签名相同的多个方法存在于一个类中吗?

      事实上有一个知识点可能大家都不知道:
      ① 方法签名 确实只有方法名+参数列表 。这毫无疑问!
      ② 我们绝对不能编写出方法签名一样的多个方法 。如果这样写程序,编译器是不会放过的。这也毫无疑问!
      ③ 最重要的一点是:JVM会用参数类型和返回类型来确定一个方法。 一旦编译器通过某种方式自己编译出方法签名一样的两个方法(只能编译器自己来创造这种奇迹,我们程序员却不能人为的编写这种代码)。JVM还是能够分清楚这些方法的,前提是需要返回类型不一样。

 

(2) 泛型类型中的方法冲突

 

       还是来看一段代码:

Java代码  收藏代码
  1. //在上面代码中加入equals方法  
  2. public class Pair<T>{  
  3.       public boolean equals(T value){  
  4.             return (first.equals(value));  
  5.       }  
  6. }  

        这样看似乎没有问题的代码连编译器都通过不了:

       【Error】    Name clash: The method equals(T) of type Pair<T> has the same erasure as equals(Object) of type Object but does not override it。

        编译器说你的方法与Object中的方法冲突了。这是为什么?

 

        开始我也不太明白这个问题,觉得好像编译器帮助我们使得equals(T)这样的方法覆盖上了Object中的equals(Object)。经过大家的讨论,我觉得应该这么解释这个问题?

 

        首先、我们都知道子类方法要覆盖,必须与父类方法具有相同的方法签名(方法名+参数列表)。而且必须保证子类的访问权限>=父类的访问权限。这是大家都知道的事实。

        然后、在上面的代码中,当编译器看到Pair<T>中的equals(T)方法时,第一反应当然是equals(T)没有覆盖住父类Object中的equals(Object)了。

        接着、编译器将泛型代码中的T用Object替代(擦除)。突然发现擦除以后equals(T)变成了equals(Object),糟糕了,这个方法与Object类中的equals一样了。基于开始确定没有覆盖这样一个想法,编译器彻底的疯了(精神分裂)。然后得出两个结论:①坚持原来的思想:没有覆盖。但现在一样造成了方法冲突了。   ②写这程序的程序员疯了(哈哈)。

 

        再说了,拿Pair<T>对象和T对象比较equals,就像牛头对比马嘴,哈哈,逻辑上也不通呀。

 

(3) 没有泛型数组一说

 

      Pair<String>[] stringPairs=new Pair<String>[10];

      Pair<Integer>[] intPairs=new Pair<Integer>[10];

      这种写法编译器会指定一个Cannot create a generic array of Pair<String>的错误

 

      我们说过泛型擦除之后,Pair<String>[]会变成Pair[],进而又可以转换为Object[];

      假设泛型数组存在,那么

            Object[0]=stringPairs[0]; Ok

            Object[1]=intPairs[0]; Ok

       这就麻烦了,理论上将Object[]可以存储所有Pair对象,但这些Pair对象是泛型对象,他们的类型变量都不一样,那么调用每一个Object[]数组元素的对象方法可能都会得到不同的记过,也许是个字符串,也许是整形,这对于JVM可是无法预料的。

 

      记住: 数组必须牢记它的元素类型,也就是所有的元素对象都必须一个样,泛型类型恰恰做不到这一点。即使Pair<String>,Pair<Integer>... 都是Pair类型的,但他们还是不一样。

 


总结:泛型代码与JVM 
    ① 虚拟机中没有泛型,只有普通类和方法。
    ② 在编译阶段,所有泛型类的类型参数都会被Object或者它们的限定边界来替换。(类型擦除)
    ③ 在继承泛型类型的时候,桥方法的合成是为了避免类型变量擦除所带来的多态灾难。


别再手动巡检了!vRealize Operations Manager 8.x 自动化报告配置全攻略(附模板下载) 本文详细介绍了如何利用vRealize Operations Manager 8.x实现自动化巡检报告配置,大幅提升虚拟化运维效率。通过模板定制、智能调度和自动分发等功能,帮助运维团队告别手动生成报告的繁琐工作,实现90%以上的时间节省。文章还提供了实用的配置技巧和最佳实践,助力企业构建高效的自动化报告系统。 阅读详情

相关推荐

芯片设计中的STA实战:如何避免时序违例(含常见问题解析)

本文深入探讨了芯片设计中静态时序分析(STA)的实战应用,重点解析了如何避免时序违例。文章从STA的核心逻辑与约束制定出发,强调了将时序意识植入RTL编码与综合阶段的重要性,并详细介绍了后端物理实现中的时序收敛策略。同时,针对跨时钟域路径误报、多工艺角违例等常见问题提供了深度解析与调试技巧,旨在帮助IC设计工程师系统性地掌握时序分析与优化方法。

open4的博客 515

Java-桥方法

1.引入 桥方法的来源是源自于Java的泛型,因为Java的泛型擦除而导致在多态的时候会出现问题而引入了桥方法 2.类型擦除 Java在语法中虽然存在泛型的概念,但是在虚拟机中却没有泛型的概念,虚拟机中所有的类型都是普通类。无论何时定义一个泛型类型,编译后类型会被都被自动转换成一个相应的原始类型。 但是要注意,泛型擦除其实只是擦除的字节码中code属性部分的泛型,classMetadata上还是会...

P19777的博客 2369

【全新官方接口】php检测微信域名拦截,检测微信域名被封

你的域名在微信被拦截的3种常见情况 1、域名因违规或有风险被拦截(红色拦截) 2、类似taobao.com,douyin.com这种的恶意竞争式拦截,淘宝、抖音等微信的竞争对手一般都是白色拦截(白色拦截) 3、有一个中间页的拦截,需要多次点击按钮才可以跳转到你的域名的,多是一些新型域名后缀,例如.top .xyz .link后缀,这种情况,备案后可以解决(备案拦截) 通过php get_headers()函数就可以分析到结果 这个下标为6是一个URL,在微信访问后正是这个讨厌的页面! 如果切换为

TANKING,开源开发者! 2880

【解惑】JVM如何理解Java泛型类

//泛型代码 public class Pair&lt;T&gt;{ private T first=null; private T second=null; public Pair(T fir,T sec){ this.first=fir; this.second=sec; } ...

ccnunlp的专栏 400

量子城域网系列(六):从星型到环形,解析QKD网络拓扑的演进与选型

本文深入解析了量子密钥分发网络从星型到环形等主流拓扑结构的演进历程与核心原理。针对量子城域网的实际部署,文章详细对比了星型、总线型和环形拓扑的优缺点,并结合政务云、金融网点等典型应用场景,提供了实战选型指南,帮助读者在物理限制、安全需求与成本效率间找到最佳平衡。

weixin_29197835的博客 111

【VAE学习笔记】全面通透地理解VAE(Variational Auto Encoder)

完整笔记:http://www.gwylab.com/note-vae.html 李宏毅老师的教程视频:https://www.bilibili.com/video/av15889450/?p=33 —————————————————————————————————   依据李宏毅老师的讲解,我整理了一番VAE的笔记。先简单介绍一下VAE,VAE作为一个生成模型,其基本思路是很容易理解的:把一堆真...

a312863063的博客 14万+

Java泛型中的桥方法(Bridge Method)

前几天在看 Java 泛型的时候,发现了一个有趣的现象。就是在某些情况下,编译器在编译我们的类文件的时候会帮我们自动生成某些方法,称作桥方法。 我们知道 Java 中的泛型在编译为 class 的时候会把泛型擦除,也就是说你写的 到最后 class 文件中其实都是 Object,看下面代码示例: public class A<T> { private T value; ...

大兵的博客 2338

SAP固定资产原值与净值取值逻辑全解析:从数据表到ABAP实战

在企业资源计划(ERP)系统中,固定资产的价值管理是财务会计的核心模块,其关键在于准确核算资产原值与净值。从技术原理上看,这涉及对资产全生命周期业务(如购置、折旧、转移、报废)的财务建模,以及底层数据表(如ANLC、ANEP)的精准关联与计算。掌握这一逻辑的技术价值在于,能够确保财务报表数据的准确性,满足合规性要求,并为管理决策提供可靠依据。在SAP ECC或S/4HANA系统的应用场景中,ABAP开发者与财务顾问常需通过编写报表程序,从复杂的数据结构中提取资产价值信息。本文聚焦于SAP资产会计模块,深入剖

weixin_30984001的博客 397

java泛型——桥方法

【0】README0.1)以下内容转自: http://www.cnblogs.com/ggjucheng/p/3352519.html【1】泛型约束和局限性—— 类型擦除所带来的麻烦1.1)继承泛型类型的多态麻烦。(—— 子类没有覆盖住父类的方法 ) 看看下面这个类SonPair class SonPair extends Pair<String>{ public void

PacosonSWJTU的博客 5799

sentaurus浪涌仿真sdevice代码解释(4)

9. `Plot(FilePrefix="Surge_n@node@" time=(Range=(0 @<0+10E-3>@) Intervals=10) NoOverWrite)`:设置了一个绘图过程,用于在瞬态分析过程中绘制结果,文件名前缀为`Surge_n@node@`,时间范围从0到`10E-3`秒,每隔10个时间点绘制一次,且不覆盖之前的结果。同样设置了最大迭代次数和线搜索阻尼系数。这个过程将模拟从`0`到`10E-3`秒内器件的行为,并且会在不同的时间点记录和绘制电流和电势的数据。

qq_52640792的博客 1660

AI测试干货:让大语言模型稳定输出 JSON 的四种实现方案

用大语言模型做测试用例生成、数据提取时,最头疼的就是让它老老实实返回 JSON。你以为它会给你 `{"name":"张三"}`,结果它来一句"好的,这是生成结果:```json...",或者干脆在 JSON 里加注释,直接把你的 `json.loads()` 干崩溃。 模型本质上是个话痨,天生爱解释。但我们要的是结构化数据,不是聊天记录。我整理了四种实战方案,从最简单的提示词技巧到工程化框架,覆盖了从快速验证到生产环境的各种场景。

blues_C的博客 957

什么是桥方法?

*桥方法(Bridge Method)**是由 Java 编译器在编译期间自动生成的合成方法,用于解决与泛型和类型擦除相关的类型兼容性问题。桥方法的生成主要是为了确保在类型擦除后仍然保持类型安全性和多态性,特别是在涉及泛型类型参数的类和接口之间的继承关系时。

qq_43657590的博客 609

SAP ABAP Append structure 介绍

所有在 append structure 上增强的字段都是 append structure 的一部分,这意味着对这些增强所做的任何更改仅仅施加在 append structure 上,而不会影响被增强的 ABAP DDIC 结构。这同样适用于 transport. 激活数据库表或结构时,会找到所有关联的 append structure,并将它们的组件附加到数据库表或结构。Append structure(附加结构)是 ABAP 字典中附加到另一个结构或数据库表并将其组件附加到它们的结构。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1166

Java JVM如何理解Java泛型类

/泛型代码 public class Pair{ private T first=null; private T second=null; public Pair(T fir,T sec){ this.first=fir; this.second=sec; } public T

Stroller 597

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

JVM如何理解Java泛型类(转)

一个很典型的泛型(generic)代码。T是类型变量,可以是任何引用类型: public class Pair<T>{ private T first=null; private T second=null; public Pair(T fir,T sec){ this.f...

Zjg13835813856的博客 146

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

上一篇: Java中HashMap遍历的两种方式
下一篇: squid,nginx,lighttpd反向代理的区别
zljjava
博客等级 码龄15年 155粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值