[英]Different return value types in implementation of generic methods
今天我偶然發現了一些我甚至無法編譯的Java代碼。 減少到最低限度,它看起來像這樣:
import java.util.List;
interface A {
<T> List<String> foo();
}
interface B {
<T> List<Integer> foo();
}
class C implements A, B {
@Override
public List<?> foo()
{
return null;
}
}
乍一看, A
和B
中的foo
方法的類型參數<T>
看起來是不必要的,因為在其他任何地方都沒有使用T
無論如何,我發現這在允許沖突的返回值類型在同一個實現中共存起着至關重要的作用:如果省略了一個或兩個<T>
,代碼就不會編譯。 這里的非工作版本:
import java.util.List;
interface A {
List<String> foo();
}
interface B {
List<Integer> foo();
}
class C implements A, B {
@Override
public List<?> foo()
{
return null;
}
}
我不需要修復上面的代碼片段,因為這些只是我用來解釋我的觀點的例子。 我只是很想知道編譯器為什么表現不同。 有人可以解釋一下這些規則究竟有什么不同嗎?
雖然第一個示例編譯,但它會給出一個未經檢查的轉換警告:
// Type safety: The return type List<?> for foo() from the type C needs
// unchecked conversion to conform to List<String>
public List<?> foo()
{
return null;
}
這里發生的是通過聲明類型參數, A.foo()
和B.foo()
是通用方法 。 然后,重寫的C.foo()
省略了該類型參數。 這與使用原始類型類似,基本上“選擇退出”該方法簽名的泛型類型檢查。 這導致編譯器使用繼承方法的擦除 : List<String> foo()
和List<Integer> foo()
都變為List foo()
,因此可以由C.foo()
。
您可以看到,通過在C.foo()
聲明中保留type參數,將會出現預期的編譯器錯誤:
// The return type is incompatible with A.foo()
public <T> List<?> foo()
{
return null;
}
同樣,如果任一接口方法未聲明類型參數,則從覆蓋中省略類型參數將無法“退出”該方法的泛型類型檢查,並且返回類型List<?>
仍然不兼容。
JLS§8.4.2中介紹了此行為:
子簽名的概念旨在表達兩種方法之間的關系,這兩種方法的簽名不相同,但可以覆蓋另一種方法。 具體來說,它允許其簽名不使用泛型類型的方法覆蓋該方法的任何泛化版本。 這很重要,因此庫設計者可以獨立於定義庫的子類或子接口的客戶端自由地生成方法。
Angelika Langer的泛型常見問題解答在她的部分中擴展了這種行為。 非泛型方法是否可以覆蓋泛型方法? :
現在,讓我們探討一個非泛型子類型方法覆蓋泛型超類型方法的示例。 如果簽名的擦除相同,則非泛型子類型方法被認為是通用超類型方法的覆蓋版本。
示例(覆蓋通用超類型方法的非泛型子類型方法):
class Super { public <T> void set( T arg) { ... } public <T> T get() { ... } } class Sub extends Super { public void set( Object arg) { ... } // overrides public Object get() { ... } // overrides with unchecked warning }
warning: get() in Sub overrides <T>get() in Super; return type requires unchecked conversion found : Object required: T public Object get() {
這里子類型方法具有簽名,即
set(Object)
和get()
,它們與超類型方法的擦除相同。 這些類型擦除的簽名被認為是覆蓋等效的。在
get
方法的情況下有一個缺陷:我們收到一個未經檢查的警告,因為返回類型並不真正兼容。 子類方法get
的返回類型是Object
,超類型方法get的返回類型是無界類型參數。 子類型方法的返回類型既不與超類型方法的返回類型相同,也不是它的子類型; 在這兩種情況下,編譯器都樂於接受返回類型兼容。 相反,子類型方法的返回類型Object
可以通過未經檢查的轉換轉換為超類型方法的返回類型。 未經檢查的警告表示無需編譯器和虛擬機執行類型檢查。 換句話說,未經檢查的操作不是類型安全的。 在可轉換返回類型的情況下,有人必須確保子類型方法的返回值與超類型方法的返回類型類型兼容,但除了程序員之外沒有人可以確保這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.