Java中的Bridge方法

news/2024/5/19 2:44:09 标签: java, 反射, bridge方法
今天在Java中字节码的格式的时候,发现method_info中的access_flags中竟然定了ACC_BRIDGE的值。网上搜了一下,大概理解它的意思了,先记之。



首先是在什么情况下会生成bridge方法(2):

bridge method may be created by the compiler when extending a parameterized type whose methods have parameterized arguments.

这是在网上找到的有人贴出来的一段话,但是感觉这段话说的并不是很明白。首先bridge方式是由编译器产生的,因而在源代码中也没有bridge的关键字。然后只有在以具体类型继承自一个泛型类,同时被继承的泛型类包含了泛型方法。比如看以下的例子:

abstract class A<T> {
public abstract T method1(T arg);
public abstract T method2();
}

class B extends A<String> {
public String method1(String arg) {
return arg;
}
public String method2() {
return "abc";
}
}

class C<T> extends A<T> {
public T method1(T arg) {
return arg;
}

public T method2() {
return null;
}
}





他们生成的.class文件如下:

A.class

abstract class org.levin.insidejvm.miscs.bridgemethod.A {

public abstract java.lang.Object method1(java.lang.Object arg0);

public abstract java.lang.Object method2();

}

B.class

class org.levin.insidejvm.miscs.bridgemethod.B extends org.levin.insidejvm.miscs.bridgemethod.A {

public java.lang.String method1(java.lang.String arg);

0 aload_1 [arg]

1 areturn

public java.lang.String method2();

0 ldc <String "abc"> [20]

2 areturn

public bridge synthetic java.lang.Object method2();

0 aload_0 [this]

1 invokevirtual org.levin.insidejvm.miscs.bridgemethod.B.method2() : java.lang.String [23]

4 areturn

public bridge synthetic java.lang.Object method1(java.lang.Object arg0);

0 aload_0 [this]

1 aload_1 [arg0]

2 checkcast java.lang.String [26]

5 invokevirtual org.levin.insidejvm.miscs.bridgemethod.B.method1(java.lang.String) : java.lang.String [28]

8 areturn

}

C.class

class org.levin.insidejvm.miscs.bridgemethod.C extends org.levin.insidejvm.miscs.bridgemethod.A {

public java.lang.Object method1(java.lang.Object arg);

0 aload_1 [arg]

1 areturn

public java.lang.Object method2();

0 aconst_null

1 areturn

}

可以看到B中生成了两个bridge方法,而C中则没有。事实上,由于Java中泛型有擦除的机制,因而在编译A类的时候,它里面定义的方法都是以Object类型来表示了,因而如果没有bridge方法,B类根本没有覆盖A类中的abstract方法。正因为有bridge方法的存在,才使得B类可以编译通过。而C类由于在编译时所有的泛型也都是通过Object类来表达的,因而它实现的也是A类中的abstract方法,因而不用再生成bridge方法了。



事实上B类中的bridge方法在调用也有一些区别:

public static void main(String[] args) {
B b = new B();
b.method1("abc");
A<String> a = new B();
a.method1("abc");
}



这段方法的字节码如下:

0 new org.levin.insidejvm.miscs.bridgemethod.B [16]

3 dup

4 invokespecial org.levin.insidejvm.miscs.bridgemethod.B() [18]

7 astore_1 [b]

8 aload_1 [b]

9 ldc <String "abc"> [19]

11 invokevirtual org.levin.insidejvm.miscs.bridgemethod.B.method1(java.lang.String) : java.lang.String [21]

14 pop

15 new org.levin.insidejvm.miscs.bridgemethod.B [16]

18 dup

19 invokespecial org.levin.insidejvm.miscs.bridgemethod.B() [18]

22 astore_2 [a]

23 aload_2 [a]

24 ldc <String "abc"> [19]

26 invokevirtual org.levin.insidejvm.miscs.bridgemethod.A.method1(java.lang.Object) : java.lang.Object [25]

29 pop

30 return

以上的代码可以看出b变量调用的method1(String)的方法,而a变量调用的却是method1(Object)方法。这种区别也正式因为bridge方法提供的支持才实现的。



事实上,bridge方法还会在另外一种情况下产生(2):

在Java 1.4中,子类若要重写父类某个方法,那么子类的方法和父类的方法签名必须完全一致,包括方法名、参数类型以及返回值;而到Java 1.5中,该机制变成,如果子类中某个方法的方法名和参数类型和父类某方法一致,并且子类该方法的返回值是父类相应方法返回值的类型或其子类型,那么该子类方法也可以重写父类中相应的方法。参看以下例子:

class E {

}

class F extends E {

}

class X {
public E getE() {
return new E();
}
}

class Y extends X {
@Override
public F getE() {
return new F();
}
}



以上代码是可以编译通过的。让我们再来查看一下Y的字节码:

class org.levin.insidejvm.miscs.bridgemethod.Y extends org.levin.insidejvm.miscs.bridgemethod.X {

public org.levin.insidejvm.miscs.bridgemethod.F getE();

0 new org.levin.insidejvm.miscs.bridgemethod.F [16]

3 dup

4 invokespecial org.levin.insidejvm.miscs.bridgemethod.F() [18]

7 areturn

public bridge synthetic org.levin.insidejvm.miscs.bridgemethod.E getE();

0 aload_0 [this]

1 invokevirtual org.levin.insidejvm.miscs.bridgemethod.Y.getE() : org.levin.insidejvm.miscs.bridgemethod.F [20]

4 areturn

}

从字节码上,我们可以看出语法本身事实上并没有发生变化,变化的只是编译器做的支持,它为重载方法重新生成了一个返回E而不是F的bridge方法

从调用的字节码上可以更加明显的看出语法没有发生变化这一点:

public static void main(String[] args) {
X x = new Y();
x.getE();
}



字节码如下:

public static void main(java.lang.String[] args);

0 new org.levin.insidejvm.miscs.bridgemethod.Y [16]

3 dup

4 invokespecial org.levin.insidejvm.miscs.bridgemethod.Y() [18]

7 astore_1 [x]

8 aload_1 [x]

9 invokevirtual org.levin.insidejvm.miscs.bridgemethod.X.getE() : org.levin.insidejvm.miscs.bridgemethod.E [19]

12 pop

13 return

该字节码中x.getE()方法事实上调用的就是生成的bridge方法(E getE())方法,而不是用户定义的F getE()方法。

这种重载机制在某些,不同子类某个函数的返回值是不一样的,但是他们都需要重写父类中方法,以可以在某个点上通过父类实例统一调用。只是这种机制就需要返回值必须是继承于同一个类。事实上,这种方式在没有引入这种重写机制的时候也是可以实现的,只是现在Java在编译器层面上提供了支持。

摘自:http://www.blogjava.net/DLevin/archive/2011/06/23/352917.html

http://www.niftyadmin.cn/n/1052516.html

相关文章

XENAPP单一服务器全功能解决方案-WI服务器

WI服务器的安装 添加角色 选择 Web Interface 角色 配置WEB interface 创建站点 选择站点类型XenAppWeb 设置为IIS默认站点 选择身份验证方式 开始创建站点 创建完成 添加服务器场 配置身份验证方法 转载于:https://blog.51cto.com/88asd/819314

applet 加载提示

<script language"JavaScript"><!-- Hide script from old browsers  function init(){   // Microsoft Internet Explorer   if (document.all){   document.all.loading.style.visibility"hidden";   document.all.myapplet.style.vis…

JS/jquery实现鼠标控制页面元素显隐

最近网站要上一个活动广告横幅&#xff0c;当用户鼠标划过时显隐二维码。像这种鼠标事件控制页面元素显隐的情况&#xff0c;码农们会经常遇到&#xff0c;可以通过javascript或jquery代码实现&#xff0c;下面就几种常见需求一起归纳一下。 mouseout和mouseleave 对于鼠标指针…

Linux下查看磁盘空间使用

1>df是以磁盘分区为单位来查看磁盘的使用情况&#xff1a; # df -lh Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 50G 14G 34G 30% / tmpfs 3.8G 0 3.8G 0% /dev/shm /dev/sda1 485M 32M 429M 7% /boot /dev/mapper/VolGroup-lv_home 42G 4.5…

android用于打开各种文件的intent

import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.net.Uri.Builder; import java.io.File; import android.content.Intent; //自定义android Intent类&#xff0c; //可用于获取打开以下文件的inte…

maven导入sqlserver驱动jar包依赖包到本地仓库

maven导入sqlserver驱动jar包依赖包到本地仓库maven项目使用sqlserver的依赖&#xff0c;先下载一个sqlserver的驱动&#xff0c;网址&#xff1a;https://www.microsoft.com/en-us/download/confirmation.aspx?id11774 然后解压找到sqljdbc42.jar 我用的jre8/sqljdbc42.jar …

关于raft算法

列出一些比较好的学习资料&#xff0c; 可以经常翻一番&#xff0c;加深印象 0 raft官方git 1 raft算法动画演示 2 Raft 为什么是更易理解的分布式一致性算法 3 raft一致性算法 4 Raft算法国际论文全翻译 5 go-raft实现 转载于:https://www.cnblogs.com/myd620/p/9052…

写论文神器

隆重推荐两个写论文神器&#xff1a;Academic Phase Bank : 汇集了所有论文需要用到的转接语句引导语句总结语句。 帮你写出最地道的学术英语 http://www.phrasebank.manchester.ac.uk/sources.htm 曼大出品 神器2&#xff1a;British National Corpus 英式英语权威语料库 收录…