上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0

          今天在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日
          注:這些文章都是前些時候寫的,之前博客很亂,也都是隨便貼一些自己寫的或轉載的,還有一些則是沒有貼出來過的。現在打算好好整理一下,完整的記錄自己的一些學習歷程,而每次看到過去的時間,則讓我想起以前的日子,因而我對時間一直是很重視的,所以每篇都著名寫的日期,直到最先的文章出現。:)

          posted on 2011-06-23 23:54 DLevin 閱讀(5175) 評論(1)  編輯  收藏 所屬分類: Core Java

          FeedBack:
          # re: Java中的Bridge方法
          2014-01-22 10:19 | gelato
          好  回復  更多評論
            
          主站蜘蛛池模板: 博客| 五常市| 抚松县| 武胜县| 丹东市| 福鼎市| 昌黎县| 曲松县| 兴城市| 石首市| 唐山市| 吉首市| 四会市| 沙湾县| 商水县| 曲靖市| 名山县| 富顺县| 旺苍县| 汝城县| 资中县| 屏南县| 观塘区| 来凤县| 永福县| 运城市| 万山特区| 浠水县| 司法| 龙州县| 西乡县| 日照市| 咸阳市| 襄汾县| 洪湖市| 武穴市| 岳阳县| 清丰县| 昭觉县| 老河口市| 沙河市|