[英]Java generic method cannot call another generic method with looser constraint and return its value
我想這樣做:
<T extends java.util.Date> T a(@Nonnull T... dates) {
return b(dates); // compile error
}
<T extends Comparable<T>> T b(T... comparables) {
return comparables[0];
}
但它無法編譯,除非我中插入一個投a
:
<T extends java.util.Date> T a(@Nonnull T... dates) {
return (T) b(dates); // warning about unsafe cast in IntelliJ
}
<T extends Comparable<T>> T b(T... comparables) {
return comparables[0];
}
有趣的是,如果我從a
刪除通用名稱,則可以使用:
java.util.Date a(java.util.Date... dates) {
return b(dates);
}
<T extends Comparable<T>> T b(T... comparables) {
return comparables[0];
}
而且,如果我將原始代碼移植到Kotlin,它也可以工作(這使我認為這是Java的局限性,而不是根本上不可知的東西):
fun <T: java.util.Date> a(dates: Array<T>): T {
return b(dates);
}
fun <T: Comparable<T>> b(comparables: Array<T>): T {
return comparables[0];
}
我的問題是: Java的類型系統有什么特殊之處可阻止此類型的編譯? 在我看來,Java編譯器可以只在后台插入強制類型轉換(據我了解,這就是在其他情況下實現泛型的方式)。
無需強制轉換即可將其編譯。 相反,當指定T
必須擴展Comparable<T>
時,可以使用有界通配符:
<T extends java.util.Date> T a(T... dates) {
return b(dates); // compiles fine
}
<T extends Comparable<? super T>> T b(T... comparables) {
return comparables[0];
}
注意Comparable<? super T>
Comparable<? super T>
而不是Comparable<T>
。
正如Johannes Kuhn在其評論中指出的那樣, Date
的子類將隱式實現Comparable<Date>
而不是Comparable<DateSubclass>
,因此需要Comparable<? super T>
Comparable<? super T>
。
有關更多信息,請參見: 什么是PECS(生產者擴展了超級消費者)?
問題在於,可以使用以下類來調用第一個方法:
class MyDate extends Date {}
然后,第一種方法中的T
推斷為MyDate
,但第二種方法中的T
不能為MyDate
,因為MyDate
不擴展Comparable<MyDate>
-它僅擴展Comparable<Date>
...
因此,編譯錯誤的根本原因是Java泛型是不變的。 這就是為什么Kotlin(其泛型支持聲明站點差異)接受代碼而沒有問題的原因。
要在Java中解決此問題,可以使用通配符類型,如Jacoc G.的答案所示。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.