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而不是Fbridge方法。

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

     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在编译器层面上提供了支持。

                                                                                                                    于2010年10月3日
注:这些文章都是前些时候写的,之前博客很乱,也都是随便贴一些自己写的或转载的,还有一些则是没有贴出来过的。现在打算好好整理一下,完整的记录自己的一些学习历程,而每次看到过去的时间,则让我想起以前的日子,因而我对时间一直是很重视的,所以每篇都著名写的日期,直到最先的文章出现。:)

相关文章
|
3月前
|
前端开发 JavaScript Java
Java 开发中 Swing 界面嵌入浏览器实现方法详解
摘要:Java中嵌入浏览器可通过多种技术实现:1) JCEF框架利用Chromium内核,适合复杂网页;2) JEditorPane组件支持简单HTML显示,但功能有限;3) DJNativeSwing-SWT可内嵌浏览器,需特定内核支持;4) JavaFX WebView结合Swing可完美支持现代网页技术。每种方案各有特点,开发者需根据项目需求选择合适方法,如JCEF适合高性能要求,JEditorPane适合简单展示。(149字)
346 1
|
4天前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
75 5
|
14天前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
65 11
|
10天前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
108 5
|
17天前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
2月前
|
算法 Java 开发者
Java 项目实战数字华容道与石头迷阵游戏开发详解及实战方法
本文介绍了使用Java实现数字华容道和石头迷阵游戏的技术方案与应用实例,涵盖GUI界面设计、二维数组操作、游戏逻辑控制及自动解法算法(如A*),适合Java开发者学习游戏开发技巧。
205 46
|
3月前
|
Java 索引
Java ArrayList中的常见删除操作及方法详解。
通过这些方法,Java `ArrayList` 提供了灵活而强大的操作来处理元素的移除,这些方法能够满足不同场景下的需求。
408 30
|
2月前
|
算法 Java
Java语言实现链表反转的方法
这种反转方法不需要使用额外的存储空间,因此空间复杂度为,它只需要遍历一次链表,所以时间复杂度为,其中为链表的长度。这使得这种反转链表的方法既高效又实用。
218 0
|
3月前
|
安全 Java API
Java 17 及以上版本核心特性在现代开发实践中的深度应用与高效实践方法 Java 开发实践
本项目以“学生成绩管理系统”为例,深入实践Java 17+核心特性与现代开发技术。采用Spring Boot 3.1、WebFlux、R2DBC等构建响应式应用,结合Record类、模式匹配、Stream优化等新特性提升代码质量。涵盖容器化部署(Docker)、自动化测试、性能优化及安全加固,全面展示Java最新技术在实际项目中的应用,助力开发者掌握现代化Java开发方法。
140 1
|
2月前
|
存储 Java 数据处理
Java映射操作:深入Map.getOrDefault与MapUtils方法
结合 `getOrDefault`方法的简洁性及 `MapUtils`的丰富功能,Java的映射操作变得既灵活又高效。合理地使用这些工具能够显著提高数据处理的速度和质量。开发人员可以根据具体的应用场景选择适宜的方法,以求在性能和可读性之间找到最佳平衡。
105 0

热门文章

最新文章