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

詳解Java的橋接方法

 更新時(shí)間:2020年12月18日 11:10:32   作者:萬貓學(xué)社  
這篇文章主要介紹了Java 橋接方法的相關(guān)資料,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下

什么是橋接方法?

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

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

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

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

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

協(xié)變返回類型

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

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

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

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

Child類重寫其父類Parent的get方法,Parent的get方法返回類型為Number,而Child類中g(shù)et方法返回類型為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(), 在源碼中是沒有出現(xiàn)過的,是由編譯器自動(dòng)生成的,該方法被標(biāo)記為ACC_BRIDGE和ACC_SYNTHETIC,就是我們前面所說的橋接方法。

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

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

類型擦除

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

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

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

public class Parent<T> {

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

  @Override
  void set(String str) {
  }
}

Child類在繼承其父類Parent的泛型方法時(shí),明確指定了泛型類型為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), 在源碼中是沒有出現(xiàn)過的,是由編譯器自動(dòng)生成的,該方法被標(biāo)記為ACC_BRIDGE和ACC_SYNTHETIC,就是我們前面所說的橋接方法。

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

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

public class Parent<Object> {

  void set(Object t) {
  }
}

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

如何查找橋接方法的實(shí)際方法

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

method = BridgeMethodResolver.findBridgedMethod(method);

findBridgedMethod方法是怎么實(shí)現(xiàn)的呢?我們來分析一下源碼(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<>();
    // 以方法名稱和入?yún)€(gè)數(shù)相等為篩選條件。
		MethodFilter filter = candidateMethod ->
				isBridgedCandidateFor(candidateMethod, bridgeMethod);
    // 遞歸該類及其所有父類上的所有方法,符合篩選條件就添加進(jìn)來。
		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í)際方法,則返回原來的橋連方法。
		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ì)比橋接方法的泛型類型參數(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é)以上源碼就是,通過判斷方法名、參數(shù)的個(gè)數(shù)以及泛型類型參數(shù)來獲取橋接方法的實(shí)際方法。

以上就是詳解Java的橋接方法的詳細(xì)內(nèi)容,更多關(guān)于java 橋接方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • spring單例如何改多例

    spring單例如何改多例

    這篇文章主要介紹了spring單例如何改多例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • IDEA設(shè)置允許一個(gè)類并行的方法

    IDEA設(shè)置允許一個(gè)類并行的方法

    這篇文章主要介紹了IDEA設(shè)置允許一個(gè)類并行的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • 淺談spring注解之@profile

    淺談spring注解之@profile

    這篇文章主要介紹了淺談spring注解之@profile,@profile通過配置來改變參數(shù),這里整理的詳細(xì)的用法,有興趣的可以了解一下
    2017-10-10
  • Reactor3 Map與FlatMap的區(qū)別示例詳解

    Reactor3 Map與FlatMap的區(qū)別示例詳解

    這篇文章主要為大家介紹了Reactor3 Map與FlatMap的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • SpringBoot + JPA @ManyToMany的操作要點(diǎn)說明

    SpringBoot + JPA @ManyToMany的操作要點(diǎn)說明

    這篇文章主要介紹了SpringBoot + JPA @ManyToMany的操作要點(diǎn)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 2020版IDEA整合GitHub的方法詳解

    2020版IDEA整合GitHub的方法詳解

    這篇文章主要介紹了2020版IDEA整合GitHub的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 解決idea?中?SpringBoot?點(diǎn)擊運(yùn)行沒反應(yīng)按鈕成灰色的問題

    解決idea?中?SpringBoot?點(diǎn)擊運(yùn)行沒反應(yīng)按鈕成灰色的問題

    在使用 Spring Boot 開發(fā)項(xiàng)目時(shí),可能會(huì)遇到一個(gè)問題:點(diǎn)擊運(yùn)行按鈕后,控制臺(tái)沒有任何輸出,項(xiàng)目界面也沒有顯示,這種情況可能是由多種原因?qū)е碌?,本文將介紹一些常見的解決方法,需要的朋友可以參考下
    2023-08-08
  • Spring Cache的基本使用與實(shí)現(xiàn)原理詳解

    Spring Cache的基本使用與實(shí)現(xiàn)原理詳解

    緩存是實(shí)際工作中非經(jīng)常常使用的一種提高性能的方法, 我們會(huì)在很多場(chǎng)景下來使用緩存。下面這篇文章主要給大家介紹了關(guān)于Spring Cache的基本使用與實(shí)現(xiàn)原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-05-05
  • Java基礎(chǔ)-Java編程語言發(fā)展史

    Java基礎(chǔ)-Java編程語言發(fā)展史

    這篇文章主要介紹了Java基礎(chǔ)-Java編程語言發(fā)展簡(jiǎn)史,Java源自Sun公司的一個(gè)叫Green的項(xiàng)目,其原先的目的是為家用電子消費(fèi)產(chǎn)品開發(fā)一個(gè)分布式代碼系統(tǒng),這樣就可以將通信和控制信息發(fā)給電冰箱、電視機(jī)、烤面包機(jī)等家用電器,對(duì)它們進(jìn)行控制和信息交流,需要的朋友可以參考一下
    2022-01-01
  • Java 設(shè)計(jì)模式原則之迪米特法則詳解

    Java 設(shè)計(jì)模式原則之迪米特法則詳解

    這篇文章主要介紹了Java 設(shè)計(jì)模式原則之迪米特法則詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08

最新評(píng)論

泰宁县| 开阳县| 老河口市| 武威市| 东台市| 榆林市| 武平县| 宣恩县| 花垣县| 民和| 东安县| 庄浪县| 萝北县| 永定县| 阜新市| 平安县| 儋州市| 韩城市| 方城县| 社会| 嵩明县| 囊谦县| 炉霍县| 墨脱县| 四子王旗| 黄平县| 卢氏县| 伊吾县| 巨野县| 太仓市| 彰化市| 崇义县| 临西县| 会东县| 尚志市| 德阳市| 高青县| 西充县| 石棉县| 融水| 白玉县|