為什麼Java無法推斷超類型
type-inference (4)
我們都知道Long擴展Number。 那麼為什麼不編譯。 以及如何定義“ with”方法,以便程序無需任何手動強制轉換即可進行編譯。
import java.util.function.Function;
public class Builder<T> {
static public interface MyInterface {
Number getNumber();
Long getLong();
}
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue) {
return null;//TODO
}
public static void main(String[] args) {
// works:
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// works:
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, Long.valueOf(4));
// compiles but also involves typecast (and Casting Number to Long is not even safe):
new Builder<MyInterface>().with( myInterface->(Long) myInterface.getNumber(), 4L);
// compiles but also involves manual conversion:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber().longValue(), 4L);
// compiles (compiler you are kidding me?):
new Builder<MyInterface>().with(castToFunction(MyInterface::getNumber), 4L);
}
static <X, Y> Function<X, Y> castToFunction(Function<X, Y> f) {
return f;
}
}
- 無法
<F, R> with(F, R)
推斷<F, R> with(F, R)
類型參數- 類型Builder.MyInterface中的getNumber()類型為Number,這與描述符的返回類型不兼容:Long
有關用例,請參見: 為什麼在編譯時不檢查lambda返回類型
Java編譯器通常不擅長推斷多個/嵌套的泛型類型或通配符。 通常,如果不使用輔助函數來捕獲或推斷某些類型,就無法獲得要編譯的內容。
但是,您真的需要將確切的
Function
類型捕獲為
F
嗎?
如果不是這樣,也許下面的方法(如您所見)似乎也適用於
Function
子類型。
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Builder<T> {
public interface MyInterface {
Number getNumber();
Long getLong();
}
public <R> Builder<T> with(Function<T, R> getter, R returnValue) {
return null;
}
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args) {
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
}
}
似乎編譯器已使用值4L來確定R為Long,而getNumber()返回一個Number,不一定是Long。
但是我不確定為什麼值優先於方法...
我認為最有趣的部分在於這兩行之間的區別:
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
在第一種情況下,
T
是顯式
Number
,因此
4L
也是
Number
,沒問題。
在第二種情況下,
4L
是
Long
,所以
T
是
Long
,因此您的函數不兼容,並且Java無法知道您是說
Number
還是
Long
。
這個表達式:
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
可以改寫為:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber(), 4L);
考慮到方法簽名:
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
-
R
將被推導為Long
-
F
將為Function<MyInterface, Long>
並且您傳遞了一個將被
Function<MyInterface, Number>
為
Function<MyInterface, Number>
的方法引用。這是關鍵-
編譯器應如何預測您實際上想從具有此類簽名的函數中返回
Long
?
它不會為您進行垂頭喪氣。
由於
Number
是
Long
超類,而
Number
不一定是
Long
(這就是為什麼它不編譯)的原因-您必須自己明確地強制轉換:
new Builder<MyInterface>().with(myInterface -> (Long) myInterface.getNumber(), 4L);
使
F
成為
Function<MyIinterface, Long>
或像您一樣在方法調用期間顯式傳遞泛型參數:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
並且知道
R
將被視為
Number
和代碼將被編譯。