簡體   English   中英

Java泛型中的橋接方法。 這個例子正確嗎?

[英]Bridge methods in Java generics. Is this example correct?

假設我有這個通用類:

class Item<T> {
  private T item;
  public void set(T item) {
    this.item = item;
  }
  public T get() {
    return item;
  } 
}

如果我創建2個這樣的實例:

Item<Integer> intItem = new Item<Integer>();
Item<String> stringItem = new Item<String>();

這兩個實例共享相同的原始類:

  class Item {
    private Object item;
    public void set(Object item) {
      this.item = item;
    }
    public Object get() {
      return item;
    } 
  }

現在,如果我像這樣擴展類Item:

class IntItem extends Item<Integer>{
  private Integer item;
  public void set(Integer item) {
    this.item = item;
  }
  public Integer get() {
   return item;
  } 
}

創建了以下橋接方法:

  class IntItem extends Item<Integer>{
      private Integer item;
      //Bridge method 1
      public void set(Object item) {
        this.item = (Integer) item;
      }
      public void set(Integer item) {
        this.item = item;
      }
      //Bridge method 2
      public Object get() {
       return item;
      }
      public Integer get() {
       return item;
      } 
    }

直到這里我都正確嗎? 我的問題是,為什么以及何時需要橋接方法? 您可以使用此Item類作為示例嗎?

我已經閱讀了其他答案,但是如果沒有具體示例,我仍然無法完全理解。

您幾乎正確了。 幾乎,因為橋接方法橋接方法調用,並且不重復方法實現。 您的IntItem類看起來像下面的已刪除版本(您可以使用例如javap進行驗證):

class IntItem extends Item<Integer> {

  private Integer item;

  // Bridge method 1
  public void set(Object item) {
    set((Integer) item);
  }

  public void set(Integer item) {
    this.item = item;
  }

  //Bridge method 2
  public Object get() {
   return <Integer>get(); // pseudosyntax
  }

  public Integer get() {
   return item;
  } 
}

在Java字節碼中,允許定義兩個僅在返回類型上有所不同的方法。 這就是為什么可以有兩種方法get你無法明確定義使用Java語言。 實際上,您需要在字節碼格式內的任何方法調用中命名參數類型和返回類型。

這就是為什么您首先需要橋接方法的原因。 Java編譯器對通用類型應用類型擦除。 這意味着JVM不會考慮泛型類型,因此會將Item<Integer>所有出現都視為原始Item 但是,這不適用於類型的顯式命名。 最后, ItemInt本身不再通用,因為它會覆蓋具有顯式類型的版本的所有方法,這些方法對於具有這些顯式類型的JVM而言是可見的。 因此,由於簽名不兼容, IntItem在其加糖版本中甚至不會覆蓋Item任何方法。 為了使通用類型對JVM透明,Java編譯器需要插入這些橋接方法,這些方法實際上會覆蓋原始實現,以便將調用 IntItem定義的方法。 這樣,您可以間接重寫方法並獲得預期的行為。 請考慮以下情形:

IntItem nonGeneric = new IntItem();
nonGeneric.set(42);

如前所述, IntItem::set(Integer)方法不是通用的,因為它被非通用類型的方法覆蓋。 因此,調用此方法不會涉及類型擦除。 Java編譯器只是編譯上述方法調用,以使用字節碼簽名set(Integer): void來調用該方法。 就像您期望的那樣。

但是,在查看以下代碼時:

Item<Integer> generic = ...;
generic.set(42);

編譯器不能確定generic變量是否包含IntItemItem (或任何其他兼容類)的實例。 因此,無法保證存在帶有字節碼簽名set(Integer): void 這就是Java編譯器應用類型擦除並查看Item<Integer> ,就好像它是原始類型一樣。 通過查看原始類型,將調用set(Object): void方法,該方法在Item本身上定義,因此始終存在。

結果,不能確定IntItem類是使用具有擦除類型(從Item繼承來的)的方法還是具有顯式類型的方法來調用其方法。 因此,隱式實現了橋接方法以創建單個版本的真理 通過動態調度橋,可以在IntItem的子類中重寫set(Integer) ,並且橋方法仍然有效。

橋接方法也可以用於實現方法的協變返回類型。 引入泛型時添加了此功能,因為橋接方法已經作為一個概念存在。 可以說,此功能是免費提供的。 橋方法的第三種應用是實現可見性 這是克服反射引擎的訪問限制所必需的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM