[英]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
變量是否包含IntItem
或Item
(或任何其他兼容類)的實例。 因此,無法保證存在帶有字節碼簽名set(Integer): void
。 這就是Java編譯器應用類型擦除並查看Item<Integer>
,就好像它是原始類型一樣。 通過查看原始類型,將調用set(Object): void
方法,該方法在Item
本身上定義,因此始終存在。
結果,不能確定IntItem
類是使用具有擦除類型(從Item
繼承來的)的方法還是具有顯式類型的方法來調用其方法。 因此,隱式實現了橋接方法以創建單個版本的真理 。 通過動態調度橋,可以在IntItem
的子類中重寫set(Integer)
,並且橋方法仍然有效。
橋接方法也可以用於實現方法的協變返回類型。 引入泛型時添加了此功能,因為橋接方法已經作為一個概念存在。 可以說,此功能是免費提供的。 橋方法的第三種應用是實現可見性橋 。 這是克服反射引擎的訪問限制所必需的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.