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

时间:2018-12-05 23:07:32

标签: java generics exception lambda 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) { }
} 

还有一段简单的代码:

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

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

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

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>时,它可以工作。 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)  { }
} 

1 个答案:

答案 0 :(得分:6)

有多个票证(hereherehere)包含相似的代码片段。这些票证被解析为“不是问题”,解释如下:

  

JLS 15.12.2.1:

     

表达式可能与目标类型兼容,具体取决于   遵守以下规则:

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

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

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

  

JLS 15.2.2.2:

     

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

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

最后:

  

由于method具有类型参数,因此lambda为   作为参数传递时,lambda从适用性检查中跳过   -意味着两者都适用-因此模棱两可。

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

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