1. 为什么需要 bridgeMethod
-
Java
(Java 源语言)与JVM
虚拟机 存在语义上的差异,Java
中Override
方法指的是方法参数和方法名称完全一致. 这样的操作称为Override
. 如果方法参数类型不一致(包括参数类型, 或者参数的顺序不一致), 这样的操作称为Overloading
. 两者在Java
中的判断都不包括返回值类型return type
, 即返回值类型与两者无关 -
JVM 中
, 对于同个方法名, 不同的parameter type
与return type
都可以看作Override
操作, 因为在 JVM 采用Full-Signature
(全方法签名) 来校验一个方法的合法性, 全体包括method name,parameter type,return type
- 综上,
Java
与JVM
对Override
的语义规则不一致. 为了去除语义冲突, 同时又不用修改JVM
实现的需求下,bridgeMethod
应运而生, 可以把它看作我们web
开发中常见的中间件 (中间过程处理) 来理解.
2. 打破常规
- 能生成
Java
字节码的方式有很多 不止在编译器中 如 Idea,eclipse 等 在编译器中 相同方法名称 不同的返回值编译是不能通过的 但是我们可以通过其他方式来生成符合场景的字节码文件 -
JVM
与Java
语义不一样, 举个生活中的例子, 这就好比方言和普通话,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
实现了父类SuperClass
的chifan()
方法, 相同的方法名, 相同的方法参数列表, 相同的方法参数类型, 即在Java
中, 这已经完成了Override
重写的语义定义. 但是在JVM
中, 这并不是能够认可的Override
行为. 有语义冲突. 所以创造了一个bridgeMethod
方法 - 本人目前学习到的所有博客, 对
Java
版本有特别印象 (学习其他大佬的文章中经常提到的版本) 的有 3 个. 从低到高分别是Java5
,Java7
,Java8
, 本文中涉及到的技术点主要是Java5
即Java 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 点, 我总以为Java
与JVM
之间语义是一致的, 或许更准确的说, 我一直都不知道 Java
与JVM
存在语义差异. 所以看一些质量不高的博客后总有一种似懂非懂的感觉. 学技术应该知其然更知其所以然. 如果有问题, 请大佬们指正. 帮助我进步. 谢谢.