为什么Java无法推断超类型?

juk*_*kzi 19 java type-inference

大家都知道龙延伸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;
  }

}

Run Code Online (Sandbox Code Playgroud)
  • 无法推断以下类型的参数 <F, R> with(F, R)
  • 类型Builder.MyInterface中的getNumber()类型为Number,它与描述符的返回类型不兼容:Long

有关用例,请参见:为什么在编译时不检查lambda返回类型

mic*_*alk 10

这个表达式:

new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
Run Code Online (Sandbox Code Playgroud)

可以改写为:

new Builder<MyInterface>().with(myInterface -> myInterface.getNumber(), 4L);
Run Code Online (Sandbox Code Playgroud)

考虑到方法签名:

public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
Run Code Online (Sandbox Code Playgroud)
  • R 将被推断为 Long
  • F 将会 Function<MyInterface, Long>

并且您传递了一个方法引用,该引用将被推断为Function<MyInterface, Number>这是关键- 编译器应如何预测您实际上想Long从具有此类签名的函数中返回?它不会为您进行垂头丧气。

因为Number是的超类,Long并且Number不一定是Long(这就是为什么它不编译)的原因-您必须自己显式地强制转换:

new Builder<MyInterface>().with(myInterface -> (Long) myInterface.getNumber(), 4L);
Run Code Online (Sandbox Code Playgroud)

制作FFunction<MyIinterface, Long>或像你一样的方法调用中明确地传递通用参数:

new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
Run Code Online (Sandbox Code Playgroud)

并且知道R将被视为Number和代码将被编译。


Ric*_*ick 1

编译器似乎使用值 4L 来确定 R 是 Long,并且 getNumber() 返回一个 Number,但不一定是 Long。

但我不确定为什么值优先于方法......