Lambda表达式和方法重载疑问

Luk*_*der 48 java lambda overloading jls java-8

好的,所以方法重载是一个糟糕的事情.既然已经解决了这个问题,我们假设我实际上想要重载这样的方法:

static void run(Consumer<Integer> consumer) {
    System.out.println("consumer");
}

static void run(Function<Integer, Integer> function) {
    System.out.println("function");
}
Run Code Online (Sandbox Code Playgroud)

在Java 7中,我可以使用非模糊的匿名类作为参数轻松地调用它们:

run(new Consumer<Integer>() {
    public void accept(Integer integer) {}
});

run(new Function<Integer, Integer>() {
    public Integer apply(Integer o) { return 1; }
});
Run Code Online (Sandbox Code Playgroud)

现在在Java 8中,我当然想用lambda表达式调用这些方法,我可以!

// Consumer
run((Integer i) -> {});

// Function
run((Integer i) -> 1);
Run Code Online (Sandbox Code Playgroud)

既然编译器应该能够推断出来Integer,为什么我不离开Integer呢?

// Consumer
run(i -> {});

// Function
run(i -> 1);
Run Code Online (Sandbox Code Playgroud)

但这不编译.编译器(javac,jdk1.8.0_05)不喜欢这样:

Test.java:63: error: reference to run is ambiguous
        run(i -> {});
        ^
  both method run(Consumer<Integer>) in Test and 
       method run(Function<Integer,Integer>) in Test match
Run Code Online (Sandbox Code Playgroud)

对我来说,直观地说,这没有意义.在产生返回值的lambda表达式("value-compatible")和产生void("void-compatible")的lambda表达式之间绝对没有歧义,如JLS§15.27中所述.

但是,当然,JLS是深度和复杂的,我们继承了20年的向后兼容性历史,并且有一些新的东西,如:

某些包含隐式类型的lambda表达式(第15.27.1节)或不精确的方法引用(第15.13.1节)的参数表达式被适用性测试忽略,因为在选择目标类型之前无法确定它们的含义.

来自JLS§15.12.2

上述限制可能与JEP 101没有一直实现的事实有关,这可以在这里这里看到.

题:

谁能准确地告诉我JLS的哪些部分指定了这个编译时的歧义(或者它是编译器错误)?

奖金:为什么事情这样决定?

更新:

使用jdk1.8.0_40,以上编译并正常工作

Hol*_*ger 20

我认为您在编译器中发现了这个错误:JDK-8029718(或Eclipse中的类似问题:434642).

JLS§15.12.2.1相比.确定可能适用的方法:

...

  • 如果满足以下所有条件,则lambda表达式(第15.27节)可能与函数接口类型(第9.8节)兼容:

    • 目标类型的函数类型的arity与lambda表达式的arity相同.

    • 如果目标类型的函数类型具有void返回,则lambda主体是语句表达式(§14.8)或void兼容块(§15.27.2).

    • 如果目标类型的函数类型具有(非void)返回类型,则lambda主体是表达式或值兼容块(第15.27.2节).

请注意" void兼容块"和"值兼容块" 之间的明显区别.虽然在某些情况下块可能同时存在,但第15.27.2.Lambda Body明确指出类似的表达式() -> {}是" void兼容块",因为它通常完成而不返回值.显而易见的是,它i -> {}也是一个" void兼容块".

并且根据上面引用的部分,lambda与非价值兼容的块和具有(非void)返回类型的目标类型的组合不是方法重载解析的潜在候选者.所以你的直觉是正确的,这里应该没有歧义.

模糊块的示例是

() -> { throw new RuntimeException(); }
() -> { while (true); }
Run Code Online (Sandbox Code Playgroud)

因为它们没有正常完成,但在你的问题中并非如此.

  • 这是正确的答案.该错误已得到修复 - https://bugs.openjdk.java.net/browse/JDK-8029718 (4认同)
  • @LukasEder我认为Holger的答案是完美的. (3认同)