gpt4 book ai didi

Java 8 消费者/函数 Lambda 歧义

转载 作者:IT老高 更新时间:2023-10-28 20:23:29 24 4
gpt4 key购买 nike

我有一个重载方法,它分别接受一个消费者和一个函数对象,并返回一个与相应消费者/函数匹配的泛型类型。我认为这很好,但是当我尝试使用 lambda 表达式调用任一方法时,我收到一个错误,表明对该方法的引用不明确。

基于我对 JLS §15.12.2.1. Identify Potentially Applicable Methods: 的阅读似乎编译器应该知道我的带有 void block 的 lambda 与 Consumer 方法匹配,而我的带有返回类型的 lambda 与 Function 方法匹配。

我整理了以下无法编译的示例代码:

import java.util.function.Consumer;
import java.util.function.Function;

public class AmbiguityBug {
public static void main(String[] args) {
doStuff(getPattern(x -> System.out.println(x)));
doStuff(getPattern(x -> String.valueOf(x)));
}

static Pattern<String, String> getPattern(Function<String, String> function) {
return new Pattern<>(function);
}

static ConsumablePattern<String> getPattern(Consumer<String> consumer) {
return new ConsumablePattern<>(consumer);
}

static void doStuff(Pattern<String, String> pattern) {
String result = pattern.apply("Hello World");
System.out.println(result);
}

static void doStuff(ConsumablePattern<String> consumablePattern) {
consumablePattern.consume("Hello World");
}

public static class Pattern<T, R> {
private final Function<T, R> function;

public Pattern(Function<T, R> function) {
this.function = function;
}

public R apply(T value) {
return function.apply(value);
}
}

public static class ConsumablePattern<T> {
private final Consumer<T> consumer;

public ConsumablePattern(Consumer<T> consumer) {
this.consumer = consumer;
}

public void consume(T value) {
consumer.accept(value);
}
}
}

我还找到了 similar原来是编译器错误的stackoverflow帖子。我的情况非常相似,虽然有点复杂。对我来说,这仍然看起来像一个错误,但我想确保我没有误解 lambdas 的语言规范。我正在使用 Java 8u45,它应该具有所有最新修复。

如果我将方法调用更改为包装在一个 block 中,一切似乎都可以编译,但这会增加额外的冗长性,并且许多自动格式化程序会将其重新格式化为多行。

doStuff(getPattern(x -> { System.out.println(x); }));
doStuff(getPattern(x -> { return String.valueOf(x); }));

最佳答案

这行肯定是模棱两可的:

doStuff(getPattern(x -> String.valueOf(x)));

从链接的 JLS 章节重读此内容:

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

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

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

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

对于 Consumer 你有一个 statement expression因为任何方法调用都可以用作语句表达式,即使该方法是非无效的。例如,您可以简单地这样写:

public void test(Object x) {
String.valueOf(x);
}

这毫无意义,但编译完美。你的方法可能有副作用,编译器不知道。例如,是否 List.add 总是返回 true 而没有人关心它的返回值。

当然,这个 lambda 也适用于 Function,因为它是一个表达式。因此是模棱两可的。如果你有一个表达式,但不是语句表达式,那么调用将毫无问题地映射到Function:

doStuff(getPattern(x -> x == null ? "" : String.valueOf(x)));

当你把它改成 { return String.valueOf(x); },你创建一个 value-compatible block ,因此它与 Function 匹配,但不符合 void 兼容 block 的条件。但是,您也可能遇到 block 问题:

doStuff(getPattern(x -> {throw new UnsupportedOperationException();}));

此 block 同时具有值​​兼容和 void 兼容的资格,因此您再次有歧义。另一个歧义 block 示例是无限循环:

doStuff(getPattern(x -> {while(true) System.out.println(x);}));

至于 System.out.println(x) 的情况有点棘手。它肯定符合语句表达式的条件,因此可以匹配到Consumer,但似乎它匹配到表达式以及规范说method invocation是一个表达式。然而它是有限使用的表达like 15.12.3说:

If the compile-time declaration is void, then the method invocation must be a top level expression (that is, the Expression in an expression statement or in the ForInit or ForUpdate part of a for statement), or a compile-time error occurs. Such a method invocation produces no value and so must be used only in a situation where a value is not needed.

所以编译器完全遵循规范。首先,它确定您的 lambda 主体同时被限定为表达式(即使它的返回类型为 void:15.12.2.1 也不异常(exception))和语句表达式,因此它也被视为歧义。

因此对我来说,这两个语句都根据规范进行编译。 ECJ 编译器在此代码上产生相同的错误消息。

一般来说,当您的重载具有相同数量的参数并且仅在可接受的功能接口(interface)方面存在差异时,我建议您避免重载方法。即使这些函数式接口(interface)有不同的数量(例如,ConsumerBiConsumer):你不会遇到 lambda 问题,但可能会遇到方法引用问题。在这种情况下,只需为您的方法选择不同的名称(例如,processStuffconsumeStuff)。

关于Java 8 消费者/函数 Lambda 歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30584887/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com