Lambda 表达式和方法重载的疑惑 [英] Lambda expression and method overloading doubts

查看:27
本文介绍了Lambda 表达式和方法重载的疑惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,所以方法重载是一件坏事™.现在这已经解决了,让我们假设我实际上想要重载这样的方法:

OK, so method overloading is-a-bad-thing™. Now that this has been settled, let's assume I actually want to overload a method like this:

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

static void run(Function<Integer, Integer> function) {
    System.out.println("function");
}

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

In Java 7, I could call them easily with non-ambiguous anonymous classes as arguments:

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

run(new Function<Integer, Integer>() {
    public Integer apply(Integer o) { return 1; }
});

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

Now in Java 8, I'd like to call those methods with lambda expressions of course, and I can!

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

// Function
run((Integer i) -> 1);

既然编译器应该能够推断出Integer,那我为什么不把Integer放在一边呢?

Since the compiler should be able to infer Integer, why don't I leave Integer away, then?

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

// Function
run(i -> 1);

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

But this doesn't compile. The compiler (javac, jdk1.8.0_05) doesn't like that:

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

对我来说,直觉上,这没有意义.产生返回值(值兼容")的 lambda 表达式和产生 void(void-compatible")的 lambda 表达式之间绝对没有歧义,如JLS §15.27.

To me, intuitively, this doesn't make sense. There is absolutely no ambiguity between a lambda expression that yields a return value ("value-compatible") and a lambda expression that yields void ("void-compatible"), as set out in the JLS §15.27.

当然,JLS 很深很复杂,我们继承了 20 年的向后兼容历史,还有一些新的东西,比如:

But of course, the JLS is deep and complex and we inherit 20 years of backwards compatibility history, and there are new things like:

某些参数表达式包含隐式类型的 lambda 表达式(§15.27.1)或不准确的方法引用(§15.13.1) 被适用性测试忽略,因为它们的含义直到选择了目标类型.

Certain argument expressions that contain implicitly typed lambda expressions (§15.27.1) or inexact method references (§15.13.1) are ignored by the applicability tests, because their meaning cannot be determined until a target type is selected.

来自JLS §15.12.2

上述限制可能与 JEP 101 并未全部实现有关方式,可以看出 此处此处.

The above limitation is probably related to the fact that JEP 101 wasn't implemented all the way, as can be seen here and here.

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

Who can tell me exactly what parts of the JLS specifies this compile-time ambiguity (or is it a compiler bug)?

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

Bonus: Why were things decided this way?

使用 jdk1.8.0_40,上面的编译和工作正常

With jdk1.8.0_40, the above compiles and works fine

推荐答案

我想你找到了 编译器中的这个错误:JDK-8029718(或类似的日食:434642).

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

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

  • A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:

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

  • The arity of the target type's function type is the same as the arity of the lambda expression.

如果目标类型的函数类型有一个 void 返回值,则 lambda 主体是语句表达式(第 14.8 节)或与 void 兼容的块(第 15.27.2 节).

If the target type's function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).

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

If the target type's function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).

注意void 兼容块"和值兼容块"之间的明显区别.虽然在某些情况下块可能同时存在,但 §15.27.2.Lambda Body 清楚地说明了像 () ->{} 是一个void 兼容块",因为它正常完成而不返回值.很明显 i ->{} 也是一个void 兼容块".

Note the clear distinction between "void compatible blocks" and "value-compatible blocks". While a block might be both in certain cases, the section §15.27.2. Lambda Body clearly states that an expression like () -> {} is a "void compatible block", as it completes normally without returning a value. And it should be obvious that i -> {} is a "void compatible block" too.

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

And according to the section cited above, the combination of a lambda with a block that is not value-compatible and target type with a (non-void) return type is not a potential candidate for the method overload resolution. So your intuition is right, there should be no ambiguity here.

模糊块的例子是

() -> { throw new RuntimeException(); }
() -> { while (true); }

因为它们不能正常完成,但您的问题并非如此.

as they don’t complete normally, but this is not the case in your question.

这篇关于Lambda 表达式和方法重载的疑惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆