Java中的桥方法
2016-04-11 14:35
477 查看
Java中的桥方法是合成方法(synthetic methods),合成方法对于实现Java语言特征是必需的。最广为人知的例子就是协变返回类型和泛型中的案例,在泛型中案例基方法的参数被擦除后与实际被调用的方法不同时会使用到桥方法。
首先来看一个例子:
Java代码
public class BridgeMethodOne {
public static class BMOne<T> {
public T getT() {
return null;
}
}
public static class BMTwo extends BMOne<String> {
public String getT() {
return null;
}
}
}
//多态调用getT()方法会自动使用父类方法。
事实上这仅仅是一个协变返回类型的例子,在类型擦除之后看起来和下面的片段类似:
Java代码
public class BridgeMethodOne {
public static class BMOne {
public Object getT() {
return null;
}
}
public static class BMTwo extends BMOne {
public String getT() {
return null;
}
}
}
然后在编译反编译之后,BMTwo将和下面类似:
public static class learn.generic.BridgeMethodOne$BMTwo extends learn.generic.BridgeMethodOne$BMOne {
.…
// Method descriptor #15 ()Ljava/lang/String;
// Stack: 1, Locals: 1
public java.lang.String getT();
0 aconst_null
1 areturn
// Method descriptor #16 ()Ljava/lang/Object;
// Stack: 1, Locals: 1
public bridge synthetic java.lang.Object getT();
0 aload_0 [this]
1 invokevirtual learn.generic.BridgeMethodOne$BMTwo.getT() : java.lang.String [17]
4 areturn
}
在上面可以看见有一个新的合成方法“java.lang.Object getT()”,这是源代码中没有的。这个方法作为一个桥方法,它所做的全部工作就是将调用代理到“java.lang.String getT()”。因为在JVM里,方法的返回类型是方法签名的一部分,而创建桥方法是实现协变返回类型的方式,因此编译器必须这么做。
现在再来看下面这个泛型指定的例子:
Java代码
public class BridgeMethodTwo {
public static class BMOne<T> {
public T getT(T args) {
return args;
}
}
public static class BMTwo extends BMOne<String> {
public String getT(String args) {
return args;
}
}
}
//多态调用自动使用子类方法。
在编译之后,BMTwo将被转换成如下:
public static class learn.generic.BridgeMethodTwo$BMTwo extends learn.generic.BridgeMethodTwo$BMOne {
public java.lang.String getT(java.lang.String args);
0 aload_1 [args]
1 areturn
// Method descriptor #18 (Ljava/lang/Object;)Ljava/lang/Object;
// Stack: 2, Locals: 2
public bridge synthetic java.lang.Object getT(java.lang.Object arg0);
0 aload_0 [this]
1 aload_1 [arg0]
2 checkcast java.lang.String [19]
5 invokevirtual learn.generic.BridgeMethodTwo$BMTwo.getT(java.lang.String) : java.lang.String [21]
8 areturn
}
在这里,桥方法重写了基类BMOne,它不仅做了有参数的调用,同时还执行了到“java.lang.String”的类型转换。这意味着在执行下面的代码忽略编译器的“uncheck”警告时,桥方法将抛出ClassCastException异常。
Java代码
public static void main(String[] args) {
BMOne one = new BMTwo();
one.getT(new Object());
}
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
所列的两个例子是桥方法使用中最广为人知的,但除此之外,桥方法至少还有一个用武之地,那就是被用来改变基类方法的可见性。试着查看下面的例子并尝试着猜测编辑器需要在什么地方创建桥方法。
Java代码
public class BridgeMethodThree {
static class ClassA {
public void foo() {
}
}
public static class ClassB extends ClassA {
}
public static class ClassC extends ClassA {
public void foo() {
}
}
}
如果查看ClassB编译后的文件,可以发现发现:
public static class learn.generic.BridgeMethodThree$ClassB extends learn.generic.BridgeMethodThree$ClassA {
public BridgeMethodThree$ClassB();
0 aload_0 [this]
1 invokespecial learn.generic.BridgeMethodThree$ClassA() [8]
4 return
public bridge synthetic void foo();
0 aload_0 [this]
1 invokespecial learn.generic.BridgeMethodThree$ClassA.foo() : void [15]
4 return
}
因为ClassA是包级别限制的,不能被包外访问,而ClassB是公共的,而所有继承的方法必须能够被包外所访问,因此编译器需要桥方法。注意,由于ClassC重写了“foo”方法,不再需要桥方法来增加可见性。
也许还有一些其它地方使用了桥方法,但没有关于这些的源信息。同样,也没有桥方法的定义,虽然从上述的例子已经看出来并可以猜到大致代表什么,但JLS里却没有明确的说明。尽管isBridge是java1.5之后反射API了的公共方法,JVM和JLS没有关于其的确切定义和编译器在何时、如何使用桥方法的定义。一般情况下,当一个类实现了一个参数化的接口或是继承了一个参数化的类时,需要引入桥方法。
英文参考:http://happyenjoylife.iteye.com/blog/1153964
http://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
转载 :http://jiangshuiy.iteye.com/blog/1339105
首先来看一个例子:
Java代码
public class BridgeMethodOne {
public static class BMOne<T> {
public T getT() {
return null;
}
}
public static class BMTwo extends BMOne<String> {
public String getT() {
return null;
}
}
}
//多态调用getT()方法会自动使用父类方法。
事实上这仅仅是一个协变返回类型的例子,在类型擦除之后看起来和下面的片段类似:
Java代码
public class BridgeMethodOne {
public static class BMOne {
public Object getT() {
return null;
}
}
public static class BMTwo extends BMOne {
public String getT() {
return null;
}
}
}
然后在编译反编译之后,BMTwo将和下面类似:
public static class learn.generic.BridgeMethodOne$BMTwo extends learn.generic.BridgeMethodOne$BMOne {
.…
// Method descriptor #15 ()Ljava/lang/String;
// Stack: 1, Locals: 1
public java.lang.String getT();
0 aconst_null
1 areturn
// Method descriptor #16 ()Ljava/lang/Object;
// Stack: 1, Locals: 1
public bridge synthetic java.lang.Object getT();
0 aload_0 [this]
1 invokevirtual learn.generic.BridgeMethodOne$BMTwo.getT() : java.lang.String [17]
4 areturn
}
在上面可以看见有一个新的合成方法“java.lang.Object getT()”,这是源代码中没有的。这个方法作为一个桥方法,它所做的全部工作就是将调用代理到“java.lang.String getT()”。因为在JVM里,方法的返回类型是方法签名的一部分,而创建桥方法是实现协变返回类型的方式,因此编译器必须这么做。
现在再来看下面这个泛型指定的例子:
Java代码
public class BridgeMethodTwo {
public static class BMOne<T> {
public T getT(T args) {
return args;
}
}
public static class BMTwo extends BMOne<String> {
public String getT(String args) {
return args;
}
}
}
//多态调用自动使用子类方法。
在编译之后,BMTwo将被转换成如下:
public static class learn.generic.BridgeMethodTwo$BMTwo extends learn.generic.BridgeMethodTwo$BMOne {
public java.lang.String getT(java.lang.String args);
0 aload_1 [args]
1 areturn
// Method descriptor #18 (Ljava/lang/Object;)Ljava/lang/Object;
// Stack: 2, Locals: 2
public bridge synthetic java.lang.Object getT(java.lang.Object arg0);
0 aload_0 [this]
1 aload_1 [arg0]
2 checkcast java.lang.String [19]
5 invokevirtual learn.generic.BridgeMethodTwo$BMTwo.getT(java.lang.String) : java.lang.String [21]
8 areturn
}
在这里,桥方法重写了基类BMOne,它不仅做了有参数的调用,同时还执行了到“java.lang.String”的类型转换。这意味着在执行下面的代码忽略编译器的“uncheck”警告时,桥方法将抛出ClassCastException异常。
Java代码
public static void main(String[] args) {
BMOne one = new BMTwo();
one.getT(new Object());
}
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
所列的两个例子是桥方法使用中最广为人知的,但除此之外,桥方法至少还有一个用武之地,那就是被用来改变基类方法的可见性。试着查看下面的例子并尝试着猜测编辑器需要在什么地方创建桥方法。
Java代码
public class BridgeMethodThree {
static class ClassA {
public void foo() {
}
}
public static class ClassB extends ClassA {
}
public static class ClassC extends ClassA {
public void foo() {
}
}
}
如果查看ClassB编译后的文件,可以发现发现:
public static class learn.generic.BridgeMethodThree$ClassB extends learn.generic.BridgeMethodThree$ClassA {
public BridgeMethodThree$ClassB();
0 aload_0 [this]
1 invokespecial learn.generic.BridgeMethodThree$ClassA() [8]
4 return
public bridge synthetic void foo();
0 aload_0 [this]
1 invokespecial learn.generic.BridgeMethodThree$ClassA.foo() : void [15]
4 return
}
因为ClassA是包级别限制的,不能被包外访问,而ClassB是公共的,而所有继承的方法必须能够被包外所访问,因此编译器需要桥方法。注意,由于ClassC重写了“foo”方法,不再需要桥方法来增加可见性。
也许还有一些其它地方使用了桥方法,但没有关于这些的源信息。同样,也没有桥方法的定义,虽然从上述的例子已经看出来并可以猜到大致代表什么,但JLS里却没有明确的说明。尽管isBridge是java1.5之后反射API了的公共方法,JVM和JLS没有关于其的确切定义和编译器在何时、如何使用桥方法的定义。一般情况下,当一个类实现了一个参数化的接口或是继承了一个参数化的类时,需要引入桥方法。
英文参考:http://happyenjoylife.iteye.com/blog/1153964
http://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
转载 :http://jiangshuiy.iteye.com/blog/1339105
相关文章推荐
- jackson、Gson反序列化 泛型
- JAVA泛型—— 3fe8 转
- JAVA泛型详解——转
- 编写高质量代码改善C#程序――使用泛型集合代替非泛型集合(建议20)
- 简单学习C#中的泛型方法使用
- C#通过反射创建自定义泛型
- C#泛型用法实例分析
- C语言泛型编程实例教程
- C# 泛型的简单理解(安全、集合、方法、约束、继承)分享
- C#泛型Dictionary的用法实例详解
- C#泛型和反射实例解析
- C#泛型实例详解
- .NET开发基础:从简单的例子理解泛型 分享
- RadioButtonList绑定图片及泛型Dictionary应用
- Swift编程中的泛型解析
- C#实现利用泛型将DataSet转为Model的方法
- 关于C#泛型列表List<T>的基本用法总结
- 详解C#中的泛型以及编程中使用泛型的优点
- list泛型自定义排序示例
- .NET基础之自定义泛型分析