mybatis 源码发现的一段代码, 详细查看之后, 发现事情并不是那么简单
1. 起因
阅读 mybatis 源码时, 看到这么一段代码:
1
2
3
4
5
6
7
8
9
10
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
2. 探索
从源码上字面意义上来看, 如果方法不是桥接方法, 那么 doSomething…
查看 isBridge 的源码, jdk 上是这么说的:
1
2
3
4
5
6
7
8
9
10
11
/**
* Returns {@code true} if this method is a bridge
* method; returns {@code false} otherwise.
*
* @return true if and only if this method is a bridge
* method as defined by the Java Language Specification.
* @since 1.5
*/
public boolean isBridge() {
return (getModifiers() & Modifier.BRIDGE) != 0;
}
从注释上来看, 啥也看不出来, 只强调了如果是一个 java 语言声明的桥接方法, 那么返回 true
3. why
google java bridge method
首先就是 oracle 官网给出的帖子: https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html
点进去看了看, 大致是这样解释的:
有时候, 泛型擦除会导致某些问题, 为了解决问题, 编译器生成一些假方法, 这种方法称为桥接方法
直接看例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
上面方法在进行编译的时候, 由于泛型擦除, 这将会导致编译后的两个类型在方法表上处于分裂状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Node {
public Object data;
public Node(Object data) { this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
在泛型擦除之后, 方法的签名会不匹配, 入参类型一个为 Object, 一个为 Integer, 这将会因为类型不一致导致方法没有覆盖, 为了解决问题, 保持多态的特性, java 编译器会生成一个桥接方法 来确保擦除后的子类按照预期工作, 大致如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyNode extends Node {
// Bridge method generated by the compiler
//
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}
桥接方法与父类签名一致setData(Object data), 且在调用后(override), 会调用本类的同名方法setData(Integer data)