問題應該是「爲什麼第一個可以編譯」而不是「爲什麼第二個失敗」。
兩者都壞了。
像
<T> T convert(String val, Object[] aa)
方法簽名說:「不管呼叫者替代T
,此方法將返回一個兼容的結果」。這不是很有用,因爲唯一有效的返回值是null
,但至少,編譯器會告訴您,當您嘗試在以這種方式聲明的方法內返回不兼容的結果時。
但子類覆蓋此方法類似
Long convert(final String ion, Object[] aa)
換句話說
,覆蓋,有望返回任何調用者與總是返回Long
的方法希望的方法。這首先應該感覺不對......當您返回null
時,結果仍然是兼容的,但當您返回非null
Long
值時,編譯器甚至不會提醒您,因爲Long
值與宣佈退貨類型爲Long
。
但是,編譯器應該已經發出了關於方法聲明本身的警告。爲了演示這個問題,你可以用這個聲明來編寫
String s = SupportedConversions.LONG.convert("bla", null);
並且編譯器不會對象。如上所述,基本類型聲明<T> T convert(…)
承諾返回任何呼叫者假設的T
,這裏,T
已被推斷爲String
。這顯然會在運行時中斷,當實現返回一個Long
實例。
爲什麼這個可以編譯的原因是與pre-Generics代碼的兼容性。目的是讓具有不同「生殖」狀態的圖書館互動。例如。您可以使用最近的jdk編譯Java 1.4應用程序代碼,即使某些類現在覆蓋 - 泛型方法。
因此,您的子類中不使用泛型的convert
方法被允許覆蓋基類的convert
方法。與此相反,像
Long convert(final String ion, List<Object> aa)
方法聲明是使用泛型,因此,不允許繞過通用類型系統。如果您使用原始類型List
,您又有一個非泛型聲明,可繞過泛型而不受影響。
如果您現在想說,假設這裏的泛型行爲是不合邏輯的,那麼您並不孤單。不僅因爲覆蓋方法與覆蓋的通用聲明位於相同的編譯單元(enum
聲明)之內,兩者都在enum
聲明內,這是一種在Java 5之前不存在的語法結構(引入泛型)。
此外,最重要的方法利用協變返回類型,Long
RESP。 Integer
其中刪除方法聲明的返回類型爲Object
,這也不能出現在Java 5之前的代碼中。
但是這些(仍然)是規則。您應該在這裏關注編譯器警告。如果您沒有收到警告(我知道Netbeans IDE的默認設置不嚴格),您應該嘗試啓用它們。
這段代碼沒有修正。 enum
s不可能做什麼。您可以刪除類型參數T
並讓基類型的方法聲明返回Object
,但enum
常量中的協變返回類型是不相關的,因爲它們不屬於public
API的一部分。最好的選擇是:
public interface SupportedConversions<T> {
SupportedConversions<Integer> INTEGER = (String ion, Object[] aa) -> {
return null;
};
SupportedConversions<Long> LONG = (String ion, Object[] aa) -> {
return null;
};
public abstract T convert(String val, Object[] aa);
}
resp。
public interface SupportedConversions<T> {
SupportedConversions<Integer> INTEGER = (ion, aa) -> {
return null;
};
SupportedConversions<Long> LONG = (ion, aa) -> {
return null;
};
public abstract T convert(String val, List<Object> aa);
// we can support both variants
public default T convert(String val, Object[] aa) {
return convert(val, Arrays.asList(aa));
}
}
這很奇怪,雖然我確定有一個完全合乎邏輯的理由。如果你從'List'中移除類型參數,那麼編譯器錯誤就會消失。或者,您可以將類型參數設置爲「T」。數組的版本會發出如下警告:「未檢查重寫:返回類型需要未經檢查的轉換。找到'java.lang.Long',必需'T'」 – Jeremy
是的編譯錯誤消失,如果我刪除顯式類型的列表壓倒一切的方法。 – Sparky