乐趣区

bridgeMethod-In-Java

1. 为什么需要 bridgeMethod

  • Java(Java 源语言)与 JVM 虚拟机 存在语义上的差异,JavaOverride方法指的是方法参数和方法名称完全一致. 这样的操作称为 Override. 如果方法参数类型不一致(包括参数类型, 或者参数的顺序不一致), 这样的操作称为Overloading. 两者在Java 中的判断都不包括返回值类型return type, 即返回值类型与两者无关
  • JVM 中 , 对于同个方法名, 不同的parameter typereturn type 都可以看作 Override 操作, 因为在 JVM 采用 Full-Signature(全方法签名) 来校验一个方法的合法性, 全体包括method name,parameter type,return type
  • 综上,JavaJVMOverride的语义规则不一致. 为了去除语义冲突, 同时又不用修改 JVM 实现的需求下,bridgeMethod应运而生, 可以把它看作我们 web 开发中常见的中间件 (中间过程处理) 来理解.

2. 打破常规

  • 能生成 Java 字节码的方式有很多 不止在编译器中 如 Idea,eclipse 等 在编译器中 相同方法名称 不同的返回值编译是不能通过的 但是我们可以通过其他方式来生成符合场景的字节码文件
  • JVMJava 语义不一样, 举个生活中的例子, 这就好比方言和普通话,Java就是四川方言,JVM就是普通话, 四川话:’ 你吃饭了没得, 幺儿 ’, 普通话:’ 你吃饭了吗, 儿子 or 女儿 '(注意这里的差别). 四川方言中,” 幺儿 ” 可以是儿子和女儿的统称, 这就对应 Java 中相同 method name 不区分 return type. 但是在普通话中, 需要指明 ” 儿子 ” 或者是 ” 女儿 ”. 大家都是一个意思, 只不过表达方式不一样. 普通话是中国话的标准, 规则就更严格和细致. 为了将方言翻译成普通话能理解的意思, 就要去除两者的语义差异, 这就是bridgeMethod 的意义.

3.API 相关

  • 根据 reflect 的相关方法, 我们可以获取一个 Object Class 的全部定义, 包括属性, 方法名等等.
  • 根据常规 class.getDeclaredMethod(String name,Class<?>...paramterTypes) 可以根据方法名称和参数类型获取class 中的方法, 但是这个方法对相同方法名不同返回值无效.
  • 可以通过 class.getDeclaredMethod() 获取全部的方法
  • MethodHandles.Lookup提供了更丰富更灵活的 API, 可以根据 method name,paramter type,return type 的限定条件获取一个方法

4. 代码实现

一个接口SuperClass

public interface SuperClass<T>{T chifan();
}

一个实现类SubClass

public class SubClass<String> implements SuperClass{
  @Override
  public String chifan(){return "幺儿";}
} 

JVM 中实现类 SubClass 的代码如下

public class SubClass<String> implements SuperClass{
  @Override
  public String chifan(){return "幺儿";// 这里是 Java 方言 幺儿的统称 String 类型 是 "男孩","女孩" 都行}
  
  @Override
  public Object chifan(){return (String)"幺儿";//bridgeMethod 将 "幺儿" 指定为 String 类型,"男孩", 或者是 "女孩"
  }
} 
  • Java 中, 子类 SubClass 实现了父类 SuperClasschifan()方法, 相同的方法名, 相同的方法参数列表, 相同的方法参数类型, 即在 Java 中, 这已经完成了 Override 重写的语义定义. 但是在 JVM 中, 这并不是能够认可的 Override 行为. 有语义冲突. 所以创造了一个 bridgeMethod 方法
  • 本人目前学习到的所有博客, 对 Java 版本有特别印象 (学习其他大佬的文章中经常提到的版本) 的有 3 个. 从低到高分别是 Java5,Java7,Java8, 本文中涉及到的技术点主要是Java5Java 1.5以后出现的. 所以我们来谈下 Java5 的版本特性

5. 追根溯源 Java5 特性

  • Java5 support covaraint return type 支持可变返回类型, 在 Java5 之前. 子类实现的方法名称, 方法参数, 返回类型都要相同, 才叫method Override. 具体可以看下篇文章

    https://blogs.oracle.com/sund…

  • Java5以后, 只要子类实现方法的返回类型是父类方法返回类型的子类, 也可以认作是Override .
  • Java5以后支持范型, 范型的出现是为了减少类型转换异常. 将一些运行时才能暴露的问题提前在编译时暴露. 可以参见下面的代码
List list=new ArrayList();
list.add("wcl");
list.add(2.50);

上面的代码没有问题, 但是并不好, 因为指代不明. 在 Idea 中会出现类型未检查的异常. 接着来举例.

有一个女老师, 带小学生们出去春游.

老师说:” 孩子们, 老师带你们上厕所, 走吧走吧 ”.(规则指定 女老师只能带小女孩去上厕所). 可是因为 ” 孩子们 ” 这样的称呼, 指代不明, 我这样呆萌的小男孩也会跟着老师去上厕所, 那这就尴尬了, 就会有误入女厕的情况发生.

为了避免这种情况发生. 范型出现了. 如下

List<String> list=new ArrayList();
list.add("wcl");
list.add(2.50);// error

范型的作用如上所示 就像是女老师的话变为

老师说:” 女孩子们, 老师带你们上厕所, 走吧走吧 ”. 这时呆萌的小男孩就不会跟着老师去了. 去了就要被教育

本文的 bridgeMethod 也可以用上面的例子举例.

“ 孩子们, 老师带你们上厕所 ” 这是 Java 中的语义

“ 孩子们, 老师带你们上男厕所 ” 这是 JVM 中的语义

看起来 范型的使用就是 规则限定前置 bridgeMethod 的使用 就是 规则限定后置 两者有异曲同工之妙

6. 结语

最近学习大佬的 SpringMVC: 源代码分析与实现 里面的 BridgeMethodResolver 然后学习了 bridgeMethod 以上都是本人看博客的一些总结 然后用自认为容易理解的形式 和 本人学习过程中模糊点的解释 . 特别是第 2 点, 我总以为JavaJVM之间语义是一致的, 或许更准确的说, 我一直都不知道 JavaJVM存在语义差异. 所以看一些质量不高的博客后总有一种似懂非懂的感觉. 学技术应该知其然更知其所以然. 如果有问题, 请大佬们指正. 帮助我进步. 谢谢.

退出移动版