前几天在看 Java 泛型的时候,发现了一个有趣的现象。就是在某些情况下,编译器在编译我们的类文件的时候会帮我们自动生成某些方法,称作桥方法。

我们知道 Java 中的泛型在编译为 class 的时候会把泛型擦除,也就是说你写的 <T> 到最后 class 文件中其实都是 Object,看下面代码示例:

public class A<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}
// ------------编译后-------------
public class A {private Object value;public Object getValue() {return value;}public void setValue(Object value) {this.value = value;}
}

可以看出,Java 中的泛型在编译后都变成了 Object,也可以说 Java 中的泛型其实是编译器为我们做了优化,虚拟机中是没有泛型的。

那我们接着看下面这段代码:

public class B extends A<String> {@Overridepublic void setValue(String value) {System.out.println("---B.setValue()---");}
}

我们写了一个 B 类,继承自 A 类,并重写了 setValue 方法。

我们来思考一个问题,按我们上面所说的 Java 泛型的擦除机制,实际 A 类中 setValue 方法应该是这样的:

// A 类中的 setValue 方法
public void setValue(Object value){this.value = value;
}

这个时候问题出来了,我们发现 B 类中的 setValue 方法参数与 A 类中的 setValue 方法参数不一样。按照 Java 重写方法的规则,B 类中的 setValue 方法实际上并没有重写父类中的方法,而是重载。

所以实际上 B 类中应该是有两个 setValue 方法,一个自己的,一个继承来的:

// 自己的
public void setValue(String value){...}
// 从父类继承的
public void setValue(Object value){...}

所以在某些场景,比如反射调用 B 类中的方法的时候,就有可能会调用到从父类继承的那个 setValue 方法。

这个时候就会出现与我们意愿不一致的结果了,违反了我们重写方法的意愿了。

当然,这种情况是不会出现的,因为 Java 编译器帮我们处理了这种情况。我们来查看 B.class 字节码文件:

// class version 52.0 (52)
// access flags 0x21
// signature LA<Ljava/lang/String;>;
// declaration: B extends A<java.lang.String>
public class B extends A  {// compiled from: B.java// access flags 0x1public <init>()VL0LINENUMBER 4 L0ALOAD 0INVOKESPECIAL A.<init> ()VRETURNL1LOCALVARIABLE this LB; L0 L1 0MAXSTACK = 1MAXLOCALS = 1// access flags 0x1public setValue(Ljava/lang/String;)VL0LINENUMBER 8 L0GETSTATIC java/lang/System.out : Ljava/io/PrintStream;LDC "---B.setValue()---"INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)VL1LINENUMBER 9 L1RETURNL2LOCALVARIABLE this LB; L0 L2 0LOCALVARIABLE value Ljava/lang/String; L0 L2 1MAXSTACK = 2MAXLOCALS = 2// access flags 0x1041public synthetic bridge setValue(Ljava/lang/Object;)VL0LINENUMBER 4 L0ALOAD 0ALOAD 1CHECKCAST java/lang/StringINVOKEVIRTUAL B.setValue (Ljava/lang/String;)VRETURNL1LOCALVARIABLE this LB; L0 L1 0MAXSTACK = 2MAXLOCALS = 2
}

我们看到 B 类中有两个 setValue 方法,一个参数为 String 类型,一个参数为 Object 类型,参数为 Object 类型的就是 Java 编译器帮我们生成的桥方法,实际代码如下:

public void setValue(String value){...}
public void setValue(Object value){setValue((String)value);
}

桥方法内部其实就是调用了我们自己的 setValue 方法,这样就避免了在重写的时候我们还能调用到父类的方法。

问题还没有完,我们接着看:

public class B extends A<String> {@Overridepublic String getValue() {return super.getValue();}
}

B 类重写了 A 类中的 getValue 方法。按照泛型的擦除,父类中的 getValue 方法返回值其实是 Object。

所以其实编译器也帮我们生成了桥方法,这里就不贴字节码文件了,大家可以自己查看。编译后的 B 类其实是这样:

public class B extends A {// 自己定义的方法public String getValue(){...}// 编译器生成的桥方法public Object getValue(){return getValue();}
}

这个时候我们发现 B 类有点颠覆我们的常识了,难道一个类中允许出现方法签名相同的多个方法?

  • 方法签名确实是方法名+参数列表
  • 我们也不能在同一个类中写两个方法签名相同的方法
  • JVM 会用方法名、参数类型和返回类型来确定一个方法,所以针对方法签名相同的两个方法,返回值类型不相同的时候,JVM是能分辨的

Java泛型中的桥方法(Bridge Method)相关推荐

  1. 【04】泛型中的桥方法

    首先说一下,泛型和CPp 有着本质区别 ,CPP 时进行代码 扩展 ,但是 java 采用的机制,共有超类  Object. 1.那么需要实现的机制 ,当单纯类型的时候,会生成对应的Object 对象 ...

  2. 聊一聊Java 泛型中的通配符 T,E,K,V,?

    点击上方"方志朋",选择"设为星标" 回复"1024"获取独家整理的学习资料 作者:glmapper juejin.im/post/5d57 ...

  3. 聊一聊-JAVA 泛型中的通配符 T,E,K,V,?

    前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据 ...

  4. Java泛型中? 和 ? extends Object的异同分析

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 刘一手 来源 | 公众号「锅外的大佬」 Jav ...

  5. JAVA 泛型中的通配符 T,E,K,V,?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群",加入新技术 来源:8rr.co/2Xqx 前言 Java 泛型(generic ...

  6. Java泛型中extends和super的理解(转)

    E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类) K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定 ...

  7. 原神一面:Java 泛型中的通配符 T,E,K,V,?,你确定都了解吗?

    点击上方 "编程技术圈"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 每日英文 Sometimes, the same thing, we can ...

  8. Java泛型专题之2、聊一聊-JAVA 泛型中的通配符 T,E,K,V,?

    目录 1. 前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说 ...

  9. 编写高质量代码:改善Java程序的151个建议 (第1章 Java开发中通用的方法和准则)

    第1章 Java开发中通用的方法和准则 The reasonable man adapts himself to the world;the unreasonable one persists in ...

最新文章

  1. mysql中usage是什么权限?
  2. excel查找窗口被拉边上_你会做 Excel目录 吗?这个奇葩方法100%的人不知道
  3. 通信原理2习题课汇总(随机信号、信道、最佳接收)
  4. linux 生成密码本,Linux下CentOS7使用OTPW实现双因子密码本登录
  5. EMC存储产品分析介绍 (一)
  6. java中负数取整_Java取整,固定保留两位小数,适配负数、金融数字。
  7. 和电商有关的词语_电商描写的词语 形容“电”的词语有哪些?
  8. 荣耀高管回应“标杆”质疑:被对标学习的才能是标杆
  9. jni 从c传递map到java_java_jni 本地调用接口DLL的编写样本,涉及数组传递,相关的 和C程序均已包括。 Develop 256万源代码下载- www.pudn.com...
  10. 直接学python3_新手应该学python2还是python3?
  11. Linux开发_退格符的花样用法
  12. PAP和CHAP认证
  13. oracle自增序列
  14. (1)电源管理-S3C2440芯片电源管理模块解析
  15. SAP ABAP 工作区,内表,标题行的定义和区别
  16. java pdf转jpg清晰度_java 库 pdfbox 将 pdf 文件转换成高清图片方法
  17. 监控系统选型,一文轻松解决
  18. 三分钟解决文档编辑难题-【文档编辑命令- cat echo vi/vim tail rmdir 】
  19. linux创建ps格式文件怎么打开,ps文件扩展名,ps文件怎么打开?
  20. IOS (不越狱)网络抓包

热门文章

  1. Eaglestream平台下 取消BMC ACPI引发的host 无法power on 问题
  2. 家用电器的CCC认证流程
  3. 乐理分析笔记(二) 莫扎特 土耳其进行曲
  4. 看世界杯效应下的中东市场,开发攻略来了
  5. python单用户登录_Python 实现用户登录的简单方法
  6. 120帧手机动态壁纸_小英雄高清动态壁纸app下载-小英雄高清动态壁纸v2.6手机下载...
  7. python 输出图像尺寸_Opencv-Python:图像尺寸、图像的读取、显示、保存与复制
  8. python元组元素的提取_Python 元组拆包
  9. 48 《自私的基因》 -豆瓣评分8.6
  10. 谷歌教你25招构建一个优秀的移动网站