一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 3分鐘快速搞懂Java的橋接方法示例

3分鐘快速搞懂Java的橋接方法示例

2021-08-09 11:04萬貓學社 Java教程

這篇文章主要介紹了3分鐘快速搞懂Java的橋接方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

什么是橋接方法?

Java中的橋接方法(Bridge Method)是一種為了實現某些Java語言特性而由編譯器自動生成的方法。

我們可以通過Method類的isBridge方法來判斷一個方法是否是橋接方法。

在字節碼文件中,橋接方法會被標記為ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE用于表示該方法是由編譯器產生的橋接方法,ACC_SYNTHETIC用于表示該方法是由編譯器自動生成。

什么時候生成橋接方法?

為了實現哪些Java語言特性會生成橋接方法?最常見的兩種情況就是協變返回值類型和類型擦除,因為它們導致了父類方法的參數和實際調用的方法參數類型不一致。下面我們通過兩個例子更好地理解一下。

協變返回類型

協變返回類型是指子類方法的返回值類型不必嚴格等同于父類中被重寫的方法的返回值類型,而可以是更 “具體” 的類型。

在Java 1.5添加了對協變返回類型的支持,即子類重寫父類方法時,返回的類型可以是子類方法返回類型的子類。下面看一個例子:

?
1
2
3
4
5
public class Parent {
  Number get() {
    return 1;
  }
}
?
1
2
3
4
5
6
7
public class Child extends Parent {
 
  @Override
  Integer get() {
    return 1;
  }
}

Child類重寫其父類Parent的get方法,Parent的get方法返回類型為Number,而Child類中get方法返回類型為Integer。

將這段代碼進行編譯,再反編譯:

?
1
2
javac Child.java
javap -v -c Child.class

結果如下:

public class Child extends Parent
......省略部分結果......
  java.lang.Integer get();
    descriptor: ()Ljava/lang/Integer;
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_1
         1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
        line 5: 0

 

  java.lang.Number get();
    descriptor: ()Ljava/lang/Number;
    flags: ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokevirtual #3                  // Method get:()Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
        line 1: 0

從上面的結果可以看到,有一個方法java.lang.Number get(), 在源碼中是沒有出現過的,是由編譯器自動生成的,該方法被標記為ACC_BRIDGE和ACC_SYNTHETIC,就是我們前面所說的橋接方法。

這個方法就起了一個橋接的作用,它所做的就是把對自身的調用通過invokevirtual指令再調用方法java.lang.Integer get()。

**編譯器這么做的原因是什么呢?**因為在JVM方法中,返回類型也是方法簽名的一部分,而橋接方法的簽名和其父類的方法簽名一致,以此就實現了協變返回值類型。

類型擦除

泛型是Java 1.5才引進的概念,在這之前是沒有泛型的概念的,但泛型代碼能夠很好地和之前版本的代碼很好地兼容,這是為什么呢?

這是因為,在編譯期間Java編譯器會將類型參數替換為其上界(類型參數中extends子句的類型),如果上界沒有定義,則默認為Object,這就叫做類型擦除。

當一個子類在繼承(或實現)一個父類(或接口)的泛型方法時,在子類中明確指定了泛型類型,那么在編譯時編譯器會自動生成橋接方法,例如:

?
1
2
3
4
5
public class Parent<T> {
 
  void set(T t) {
  }
}
?
1
2
3
4
5
6
public class Child extends Parent<String> {
 
  @Override
  void set(String str) {
  }
}

Child類在繼承其父類Parent的泛型方法時,明確指定了泛型類型為String,將這段代碼進行編譯,再反編譯:

public class Child extends Parent<java.lang.String>
......省略部分結果......
  void set(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags:
    Code:
      stack=0, locals=2, args_size=2
         0: return
      LineNumberTable:
        line 5: 0

  void set(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V
    flags: ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #2                  // class java/lang/String
         5: invokevirtual #3                  // Method set:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 1: 0

從上面的結果可以看到,有一個方法void set(java.lang.Object), 在源碼中是沒有出現過的,是由編譯器自動生成的,該方法被標記為ACC_BRIDGE和ACC_SYNTHETIC,就是我們前面所說的橋接方法。

這個方法就起了一個橋接的作用,它所做的就是把對自身的調用通過invokevirtual指令再調用方法void set(java.lang.String)。

**編譯器這么做的原因是什么呢?**因為Parent類在類型擦除之后,變成這樣:

?
1
2
3
4
5
public class Parent<Object> {
 
  void set(Object t) {
  }
}

編譯器為了讓子類有一個與父類的方法簽名一致的方法,就在子類自動生成一個與父類的方法簽名一致的橋接方法。

如何獲取橋接方法的實際方法

在Spring Framework中已經實現了獲取橋接方法的實際方法的功能,就在spring-core模塊中的BridgeMethodResolver類中,像這樣直接使用就行了:

?
1
method = BridgeMethodResolver.findBridgedMethod(method);

findBridgedMethod方法是怎么實現的呢?我們來分析一下源碼(spring-core的版本為5.2.8.RELEASE):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static Method findBridgedMethod(Method bridgeMethod) {
  // 如果不是橋連方法,就直接返回原方法。
 if (!bridgeMethod.isBridge()) {
 return bridgeMethod;
 }
  // 先從本地緩存讀取,緩存中有則直接返回。
 Method bridgedMethod = cache.get(bridgeMethod);
 if (bridgedMethod == null) {
 List<Method> candidateMethods = new ArrayList<>();
    // 以方法名稱和入參個數相等為篩選條件。
 MethodFilter filter = candidateMethod ->
  isBridgedCandidateFor(candidateMethod, bridgeMethod);
    // 遞歸該類及其所有父類上的所有方法,符合篩選條件就添加進來。
 ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass()
      , candidateMethods::add, filter);
 if (!candidateMethods.isEmpty()) {
      // 如果符合篩選條件的方法個數為1,則直接采用;
      // 否則,調用searchCandidates方法再次篩選。
  bridgedMethod = candidateMethods.size() == 1 ?
   candidateMethods.get(0) :
   searchCandidates(candidateMethods, bridgeMethod);
 }
    // 如果找不到實際方法,則返回原來的橋連方法。
 if (bridgedMethod == null) {
  // A bridge method was passed in but we couldn't find the bridged method.
  // Let's proceed with the passed-in method and hope for the best...
  bridgedMethod = bridgeMethod;
 }
    // 把查找的結果放入內存緩存。
 cache.put(bridgeMethod, bridgedMethod);
 }
 return bridgedMethod;
}

我們再看一下再次篩選的searchCandidates方法是如何實現的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
 if (candidateMethods.isEmpty()) {
 return null;
 }
 Method previousMethod = null;
 boolean sameSig = true;
  // 遍歷候選方法的列表
 for (Method candidateMethod : candidateMethods) {
    // 對比橋接方法的泛型類型參數和候選方法是否匹配,如果匹配則直接返回該候選方法。
 if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) {
  return candidateMethod;
 }
 else if (previousMethod != null) {
      // 如果不匹配,則判斷所有候選方法的參數列表是否相等。
  sameSig = sameSig && Arrays.equals(candidateMethod.getGenericParameterTypes()
        , previousMethod.getGenericParameterTypes());
 }
 previousMethod = candidateMethod;
 }
  // 如果所有候選方法的參數列表全相等,則返回第一個候選方法。
 return (sameSig ? candidateMethods.get(0) : null);
}

總結以上源碼就是,通過判斷方法名、參數的個數以及泛型類型參數來獲取橋接方法的實際方法。

到此這篇關于3分鐘快速搞懂Java的橋接方法示例的文章就介紹到這了,更多相關Java 橋接內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/heihaozi/article/details/111196629

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲入口| 国产第一福利视频导航在线 | 四虎影视在线观看2413 | chinese真实incest chinese特色video chinese男性厕所撒尿合集 | 国产va免费精品高清在线 | 亚洲精品国偷拍自产在线观看蜜臀 | 四虎视屏 | 99久久精品国语对白 | 日本三级大学生17 | 97久久天天综合色天天综合色hd | 女性性色生活片免费观看 | 日韩免费一级毛片 | 国产日韩欧美一区 | 天天射天天舔 | 美女翘臀跪床被打屁股作文 | 久久无码人妻中文国产 | 亚洲国产剧情中文视频在线 | 奇米7777第四色 | 精品视频日本 | 美女班主任让我爽了一夜视频 | 午夜五月天 | 成人国产精品视频频 | 国产亚洲综合精品一区二区三区 | 99在线播放 | 亚洲香蕉伊在人在线观婷婷 | 国产福利视频一区二区微拍 | 免费观看欧美性一级 | 日日免费视频 | sao虎影院桃红视频在线观看 | 久热这里只有精品99国产6 | 二次元美女脱裤子让男人桶爽 | 狠狠插入 | 国产精品久久毛片蜜月 | 扒开双腿羞辱调教play视频 | 欧美高清免费一级在线 | 日韩在线天堂免费观看 | 色综合久久六月婷婷中文字幕 | 高人先生免费观看全集 | 国产美女操 | 国产精品第四页 | 国内精品一区二区在线观看 |