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

2022-09-04 22:41:00

让我们有一个功能接口(为了简洁起见,我省略了实现并简化了情况):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) { }
} 

还有一段简单的代码:

Functional<String> functional = (string) -> {};
functional.method("string", (string) -> 1);

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

日食

该方法对于类型是不明确的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>)

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

编辑:一个有趣的事实。当我用 替换时,它有效。似乎无法扩展,等等...default <T extends Number><T>TNumberThrowable

default <T> void method(E e, T t)  { }
default <T> void method(E e, Function<E, T> function) { }

编辑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)  { }
} 

答案 1

有多个票证(此处此处此处)包含类似的代码片段。这些票证已解决为“不是问题”,说明如下:

JLS 15.12.2.1:

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

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

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

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

JLS 15.2.2.2:

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

  • [...]
  • if 是泛型方法,并且方法调用不提供显式类型参数,则显式类型 lambda 表达式或确切的方法引用表达式,其相应的目标类型(从 m 的签名派生而来)是 的类型参数。mm

最后:

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

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

functional.method("string", (Function<String, Number>) (string) -> 1);

答案 2

推荐