為什麼Java無法推斷超類型




type-inference (4)

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");

    }
}

我們都知道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返回類型


似乎編譯器已使用值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 ,沒問題。 在第二種情況下, 4LLong ,所以 TLong ,因此您的函數不兼容,並且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 它不會為您進行垂頭喪氣。

由於 NumberLong 超類,而 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 和代碼將被編譯。





type-inference