在Java中传递lambda表达式时,方法是不明确的

Nik*_*las 12 java generics lambda exception java-8

让我们有一个功能界面Functional(为了简洁起见,我省略了实现并简化了案例):

@FunctionalInterface 
public interface Functional<E> { 

    void perform(E e);

    default <T extends Number> void method(E e, T t)  { }
    default <T extends Number> void method(E e, Function<E, T> function) { }
} 
Run Code Online (Sandbox Code Playgroud)

还有一段简单的代码:

Functional<String> functional = (string) -> {};
functional.method("string", (string) -> 1);
Run Code Online (Sandbox Code Playgroud)

为什么方法method()不明确,因为lambda作为参数传递?这应该很容易区分.

Eclipse:

该方法method(String, Function<String,Integer>)对于该类型是不明确的Functional<String>

这在IntelliJIdea上也是可重现的.

Javac输出(感谢@AndyTurner):

Main.java:21: error: reference to method is ambiguous
        functional.method("string", (string) -> 1);
                  ^
  both method <T#1>method(E,T#1) in Functional and method <T#2>method(E,Function<E,T#2>) in Functional match
  where T#1,E,T#2 are type-variables:
    T#1 extends Number declared in method <T#1>method(E,T#1)
    E extends Object declared in interface Functional
    T#2 extends Number declared in method <T#2>method(E,Function<E,T#2>)
Run Code Online (Sandbox Code Playgroud)

Main.java:21: error: incompatible types: cannot infer type-variable(s) T
        functional.method("string", (string) -> 1);
                         ^
    (argument mismatch; Number is not a functional interface)
  where T,E are type-variables:
    T extends Number declared in method <T>method(E,T)
    E extends Object declared in interface Functional
Run Code Online (Sandbox Code Playgroud)

编辑:一个有趣的事实.当我更换default <T extends Number><T>,它的工作原理.在T似乎不能扩展Number,Throwable等等...

default <T> void method(E e, T t)  { }
default <T> void method(E e, Function<E, T> function) { }
Run Code Online (Sandbox Code Playgroud)

编辑2:当我将泛型类型T设置为接口声明时,它也可以工作:

@FunctionalInterface 
public interface Functional<E, T extends Number> { 

    void get(E e);

    default void method(E e, Function<E, T> function) { }
    default void method(E e, T t)  { }
} 
Run Code Online (Sandbox Code Playgroud)

Ole*_*hov 6

有多个票证(此处,此处此处)包含类似的代码片段.这些票据被解析为"不是问题",解释如下:

JLS 15.12.2.1:

根据以下规则,表达式可能与目标类型兼容:

  • [...]
  • 如果类型变量是候选方法的类型参数,则lambda表达式或方法引用表达式可能与类型变量兼容.

因此,method在这种情况下,两种方法都可能兼容.

此外,lambda (string) -> 1与适用性无关,因为:

JLS 15.2.2.2:

m除非具有以下形式之一,否则参数表达式被认为与可能适用的方法的适用性相关

  • [...]
  • 如果m是泛型方法,并且方法调用不提供显式类型参数,则显式类型的lambda表达式或精确方法引用表达式,对应的目标类型(从m的签名派生)是其类型参数m.

最后:

由于method有一个类型参数,其中lambda作为参数传递,因此从适用性检查中跳过lambda - 意味着两者都适用 - 因此存在歧义.

可能的解决方法 - 在调用方法时强制转换参数:

functional.method("string", (Function<String, Number>) (string) -> 1);
Run Code Online (Sandbox Code Playgroud)