【问题标题】:A method is ambiguous upon passing a lambda expression in Java在 Java 中传递 lambda 表达式时方法不明确
【发布时间】:2019-05-07 14:31:51
【问题描述】:

让我们有一个函数式接口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() 模棱两可?这应该很容易区分。

Eclipse

方法method(String, Function&lt;String,Integer&gt;) 对于Functional&lt;String&gt; 类型不明确

这也可以在 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

编辑:一个有趣的事实。当我用&lt;T&gt; 替换default &lt;T extends Number&gt; 时,它可以工作。 T 似乎无法扩展 NumberThrowable 等...

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

【问题讨论】:

  • 我希望&lt;T extends Number&gt; 可以工作,而&lt;T&gt; 会失败,因为它可以与任何参数兼容。有趣的是,情况恰恰相反。作为记录,如果第二种方法有extends Number,这没有什么区别。
  • @shmosel Function 严格小于Object,但NumberFunction 是“并列”的。 (并且可能有一个扩展Number并实现Function的类。)
  • @LouisWasserman 这样的类永远无法实现/实例化为 lambda。
  • 当您从变量 Function&lt;String, ? extends Number&gt; 引用 lambda 时,它似乎也可以工作。似乎很奇怪,它无法正确推断内联时使用哪种方法。
  • 有趣的是,提供显式类型参数会使歧义消失,即functional.&lt;Number&gt;method("string", (string) -&gt; 1); 工作正常。

标签: java generics exception lambda java-8


【解决方案1】:

有多个工单(hereherehere)包含相似的代码 sn-ps。这些票被解决为“不是问题”,解释如下:

JLS 15.12.2.1:

表达式可能与目标类型兼容 遵守以下规则:

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

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

另外,lambda (string) -&gt; 1 与适用性无关,因为:

JLS 15.2.2.2:

参数表达式被认为与适用于 可能适用的方法m,除非它具有以下之一 表格

  • [...]
  • 如果m是泛型方法并且方法调用不提供 显式类型参数、显式类型的 lambda 表达式或 对应目标的精确方法引用表达式 type(源自 m 的签名)是 m 的类型参数。

最后:

因为method 有一个类型参数,其中 lambda 是 作为参数传递,从适用性检查中跳过 lambda - 表示两者都适用 - 因此有歧义。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-27
    相关资源
    最近更新 更多