最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2021年02月18日 15:16:56   作者:萬(wàn)貓學(xué)社  
這篇文章主要介紹了3分鐘快速搞懂Java的橋接方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

什么是橋接方法?

Java中的橋接方法(Bridge Method)是一種為了實(shí)現(xiàn)某些Java語(yǔ)言特性而由編譯器自動(dòng)生成的方法。

我們可以通過(guò)Method類(lèi)的isBridge方法來(lái)判斷一個(gè)方法是否是橋接方法。

在字節(jié)碼文件中,橋接方法會(huì)被標(biāo)記為ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE用于表示該方法是由編譯器產(chǎn)生的橋接方法,ACC_SYNTHETIC用于表示該方法是由編譯器自動(dòng)生成。

什么時(shí)候生成橋接方法?

為了實(shí)現(xiàn)哪些Java語(yǔ)言特性會(huì)生成橋接方法?最常見(jiàn)的兩種情況就是協(xié)變返回值類(lèi)型和類(lèi)型擦除,因?yàn)樗鼈儗?dǎo)致了父類(lèi)方法的參數(shù)和實(shí)際調(diào)用的方法參數(shù)類(lèi)型不一致。下面我們通過(guò)兩個(gè)例子更好地理解一下。

協(xié)變返回類(lèi)型

協(xié)變返回類(lèi)型是指子類(lèi)方法的返回值類(lèi)型不必嚴(yán)格等同于父類(lèi)中被重寫(xiě)的方法的返回值類(lèi)型,而可以是更 “具體” 的類(lèi)型。

在Java 1.5添加了對(duì)協(xié)變返回類(lèi)型的支持,即子類(lèi)重寫(xiě)父類(lèi)方法時(shí),返回的類(lèi)型可以是子類(lèi)方法返回類(lèi)型的子類(lèi)。下面看一個(gè)例子:

public class Parent {
  Number get() {
    return 1;
  }
}
public class Child extends Parent {

  @Override
  Integer get() {
    return 1;
  }
}

Child類(lèi)重寫(xiě)其父類(lèi)Parent的get方法,Parent的get方法返回類(lèi)型為Number,而Child類(lèi)中g(shù)et方法返回類(lèi)型為Integer。

將這段代碼進(jìn)行編譯,再反編譯:

javac Child.java
javap -v -c Child.class

結(jié)果如下:

public class Child extends Parent
......省略部分結(jié)果......
  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

從上面的結(jié)果可以看到,有一個(gè)方法java.lang.Number get(), 在源碼中是沒(méi)有出現(xiàn)過(guò)的,是由編譯器自動(dòng)生成的,該方法被標(biāo)記為ACC_BRIDGE和ACC_SYNTHETIC,就是我們前面所說(shuō)的橋接方法。

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

**編譯器這么做的原因是什么呢?**因?yàn)樵贘VM方法中,返回類(lèi)型也是方法簽名的一部分,而橋接方法的簽名和其父類(lèi)的方法簽名一致,以此就實(shí)現(xiàn)了協(xié)變返回值類(lèi)型。

類(lèi)型擦除

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

這是因?yàn)?,在編譯期間Java編譯器會(huì)將類(lèi)型參數(shù)替換為其上界(類(lèi)型參數(shù)中extends子句的類(lèi)型),如果上界沒(méi)有定義,則默認(rèn)為Object,這就叫做類(lèi)型擦除。

當(dāng)一個(gè)子類(lèi)在繼承(或?qū)崿F(xiàn))一個(gè)父類(lèi)(或接口)的泛型方法時(shí),在子類(lèi)中明確指定了泛型類(lèi)型,那么在編譯時(shí)編譯器會(huì)自動(dòng)生成橋接方法,例如:

public class Parent<T> {

  void set(T t) {
  }
}
public class Child extends Parent<String> {

  @Override
  void set(String str) {
  }
}

Child類(lèi)在繼承其父類(lèi)Parent的泛型方法時(shí),明確指定了泛型類(lèi)型為String,將這段代碼進(jìn)行編譯,再反編譯:

public class Child extends Parent<java.lang.String>
......省略部分結(jié)果......
  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

從上面的結(jié)果可以看到,有一個(gè)方法void set(java.lang.Object), 在源碼中是沒(méi)有出現(xiàn)過(guò)的,是由編譯器自動(dòng)生成的,該方法被標(biāo)記為ACC_BRIDGE和ACC_SYNTHETIC,就是我們前面所說(shuō)的橋接方法。

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

**編譯器這么做的原因是什么呢?**因?yàn)镻arent類(lèi)在類(lèi)型擦除之后,變成這樣:

public class Parent<Object> {

  void set(Object t) {
  }
}

編譯器為了讓子類(lèi)有一個(gè)與父類(lèi)的方法簽名一致的方法,就在子類(lèi)自動(dòng)生成一個(gè)與父類(lèi)的方法簽名一致的橋接方法。

如何獲取橋接方法的實(shí)際方法

在Spring Framework中已經(jīng)實(shí)現(xiàn)了獲取橋接方法的實(shí)際方法的功能,就在spring-core模塊中的BridgeMethodResolver類(lèi)中,像這樣直接使用就行了:

method = BridgeMethodResolver.findBridgedMethod(method);

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

public static Method findBridgedMethod(Method bridgeMethod) {
  // 如果不是橋連方法,就直接返回原方法。
 if (!bridgeMethod.isBridge()) {
 return bridgeMethod;
 }
  // 先從本地緩存讀取,緩存中有則直接返回。
 Method bridgedMethod = cache.get(bridgeMethod);
 if (bridgedMethod == null) {
 List<Method> candidateMethods = new ArrayList<>();
    // 以方法名稱(chēng)和入?yún)€(gè)數(shù)相等為篩選條件。
 MethodFilter filter = candidateMethod ->
  isBridgedCandidateFor(candidateMethod, bridgeMethod);
    // 遞歸該類(lèi)及其所有父類(lèi)上的所有方法,符合篩選條件就添加進(jìn)來(lái)。
 ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass()
      , candidateMethods::add, filter);
 if (!candidateMethods.isEmpty()) {
      // 如果符合篩選條件的方法個(gè)數(shù)為1,則直接采用;
      // 否則,調(diào)用searchCandidates方法再次篩選。
  bridgedMethod = candidateMethods.size() == 1 ?
   candidateMethods.get(0) :
   searchCandidates(candidateMethods, bridgeMethod);
 }
    // 如果找不到實(shí)際方法,則返回原來(lái)的橋連方法。
 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;
 }
    // 把查找的結(jié)果放入內(nèi)存緩存。
 cache.put(bridgeMethod, bridgedMethod);
 }
 return bridgedMethod;
}

我們?cè)倏匆幌略俅魏Y選的searchCandidates方法是如何實(shí)現(xiàn)的:

private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
 if (candidateMethods.isEmpty()) {
 return null;
 }
 Method previousMethod = null;
 boolean sameSig = true;
  // 遍歷候選方法的列表
 for (Method candidateMethod : candidateMethods) {
    // 對(duì)比橋接方法的泛型類(lèi)型參數(shù)和候選方法是否匹配,如果匹配則直接返回該候選方法。
 if (isBridgeMethodFor(bridgeMethod, candidateMethod, bridgeMethod.getDeclaringClass())) {
  return candidateMethod;
 }
 else if (previousMethod != null) {
      // 如果不匹配,則判斷所有候選方法的參數(shù)列表是否相等。
  sameSig = sameSig && Arrays.equals(candidateMethod.getGenericParameterTypes()
        , previousMethod.getGenericParameterTypes());
 }
 previousMethod = candidateMethod;
 }
  // 如果所有候選方法的參數(shù)列表全相等,則返回第一個(gè)候選方法。
 return (sameSig ? candidateMethods.get(0) : null);
}

總結(jié)以上源碼就是,通過(guò)判斷方法名、參數(shù)的個(gè)數(shù)以及泛型類(lèi)型參數(shù)來(lái)獲取橋接方法的實(shí)際方法。

到此這篇關(guān)于3分鐘快速搞懂Java的橋接方法示例的文章就介紹到這了,更多相關(guān)Java 橋接內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)HTTP調(diào)用的7 種方式

    SpringBoot實(shí)現(xiàn)HTTP調(diào)用的7 種方式

    本文主要介紹了SpringBoot實(shí)現(xiàn)HTTP調(diào)用的7 種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • SpringBoot集成PageHelper及使用方法詳解

    SpringBoot集成PageHelper及使用方法詳解

    這篇文章主要介紹了SpringBoot集成PageHelper及使用方法詳解,PageHelper 是一個(gè)開(kāi)源的 Java 分頁(yè)插件,它可以幫助開(kāi)發(fā)者簡(jiǎn)化分頁(yè)操作,本文提供部分相關(guān)代碼,需要的朋友可以參考下
    2023-10-10
  • Spring Boot如何集成模板引擎FreeMarker

    Spring Boot如何集成模板引擎FreeMarker

    這篇文章主要介紹了Spring Boot如何集成模板引擎FreeMarker,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Java面向?qū)ο笾^承的概念詳解

    Java面向?qū)ο笾^承的概念詳解

    這篇文章主要介紹了Java面向?qū)ο笾^承的概念詳解,Java是一種面向?qū)ο蟮木幊陶Z(yǔ)言,繼承是實(shí)現(xiàn)面向?qū)ο缶幊痰幕A(chǔ)之一。通過(guò)繼承,我們可以使代碼更具可讀性、可重用性和可維護(hù)性,從而提高程序的效率和可靠性,需要的朋友可以參考下
    2023-04-04
  • Java的HashSet源碼詳解

    Java的HashSet源碼詳解

    這篇文章主要介紹了Java的HashSet源碼詳解,HashSet底層封裝的是HashMap,所以元素添加會(huì)放到HashMap的key中,value值使用new Object對(duì)象作為value,所以HashSet和HashMap的所具有的特點(diǎn)是類(lèi)似的,需要的朋友可以參考下
    2023-09-09
  • 解決Java中SimpleDateFormat線(xiàn)程不安全的五種方案

    解決Java中SimpleDateFormat線(xiàn)程不安全的五種方案

    SimpleDateFormat 就是一個(gè)典型的線(xiàn)程不安全事例,本文主要介紹了解決Java中SimpleDateFormat線(xiàn)程不安全的五種方案,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Mybatis中where標(biāo)簽與if標(biāo)簽結(jié)合使用詳細(xì)說(shuō)明

    Mybatis中where標(biāo)簽與if標(biāo)簽結(jié)合使用詳細(xì)說(shuō)明

    mybatis中if和where用于動(dòng)態(tài)sql的條件拼接,在查詢(xún)語(yǔ)句中如果缺失某個(gè)條件,通過(guò)if和where標(biāo)簽可以動(dòng)態(tài)的改變查詢(xún)條件,下面這篇文章主要給大家介紹了關(guān)于Mybatis中where標(biāo)簽與if標(biāo)簽結(jié)合使用的詳細(xì)說(shuō)明,需要的朋友可以參考下
    2023-03-03
  • 使用Java實(shí)現(xiàn)獲取excel附件并解析

    使用Java實(shí)現(xiàn)獲取excel附件并解析

    在Java中,我們經(jīng)常需要讀取Excel文件中的數(shù)據(jù),進(jìn)行處理和分析,本文將介紹如何使用Java代碼來(lái)獲取與解析Excel文件中的附件,希望對(duì)大家有所幫助
    2025-04-04
  • mybatis對(duì)傳入基本類(lèi)型參數(shù)的判斷方式

    mybatis對(duì)傳入基本類(lèi)型參數(shù)的判斷方式

    這篇文章主要介紹了mybatis對(duì)傳入基本類(lèi)型參數(shù)的判斷方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java利用多線(xiàn)程模擬銀行系統(tǒng)存錢(qián)問(wèn)題

    Java利用多線(xiàn)程模擬銀行系統(tǒng)存錢(qián)問(wèn)題

    本文將利用Java多線(xiàn)程模擬銀行系統(tǒng)存錢(qián)問(wèn)題:使用兩個(gè)不同的線(xiàn)程向同一個(gè)賬戶(hù)存錢(qián)。文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-08-08

最新評(píng)論

恩施市| 景泰县| 秦安县| 蒙阴县| 杭锦旗| 湘西| 塔城市| 喜德县| 兴隆县| 神农架林区| 墨玉县| 沈丘县| 聂拉木县| 湖北省| 西昌市| 通城县| 鱼台县| 仲巴县| 五指山市| 昭通市| 始兴县| 陆河县| 东乌珠穆沁旗| 台中县| 西城区| 玉龙| 宁强县| 新兴县| 中卫市| 灵台县| 东台市| 吉林省| 凤山市| 温州市| 古浪县| 巴彦县| 盈江县| 玉环县| 佛坪县| 聂拉木县| 漳平市|