gpt4 book ai didi

java - 使用 lambda 和泛型时对方法的引用不明确

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

我在以下代码中遇到错误,我认为它不应该存在...使用 JDK 8u40 编译此代码。

public class Ambiguous {
public static void main(String[] args) {
consumerIntFunctionTest(data -> {
Arrays.sort(data);
}, int[]::new);

consumerIntFunctionTest(Arrays::sort, int[]::new);
}

private static <T> void consumerIntFunctionTest(final Consumer<T> consumer, final IntFunction<T> generator) {

}

private static <T> void consumerIntFunctionTest(final Function<T, ?> consumer, final IntFunction<T> generator) {

}
}

错误如下:

Error:(17, 9) java: reference to consumerIntFunctionTest is ambiguous both method consumerIntFunctionTest(java.util.function.Consumer,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous and method consumerIntFunctionTest(java.util.function.Function,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous match

错误发生在以下行:

consumerIntFunctionTest(Arrays::sort, int[]::new);

我相信应该没有错误,因为所有 Arrays::sort引用的类型为 void ,并且它们都没有返回值。如您所见,当我显式展开 Consumer<T> 时,它确实工作。 lambda 。

这真的是 javac 中的错误,还是 JLS 声明 lambda 在这种情况下无法自动展开?如果是后者,我还是觉得很奇怪,如consumerIntFunctionTest作为第一个参数 Function<T, ?>不应该匹配。

最佳答案

在你的第一个例子中

consumerIntFunctionTest(data -> {
Arrays.sort(data);
}, int[]::new);

lambda 表达式有一个与 void 兼容的 block ,它可以通过表达式的结构来识别,而无需解析实际类型。

相比之下,在示例中

consumerIntFunctionTest(Arrays::sort, int[]::new);

必须解析方法引用以确定它是否符合 void 函数 (Consumer) 或返回值函数 (Function )。这同样适用于简化的 lambda 表达式

consumerIntFunctionTest(data -> Arrays.sort(data), int[]::new);

这两者可以是 void- 兼容或值-兼容,具体取决于解析的目标方法。

问题是解决方法需要了解所需的签名,这应该通过目标类型来确定,但是在知道泛型方法的类型参数之前,目标类型是未知的。虽然理论上两者都可以同时确定,但是(仍然非常复杂的)过程在规范中得到了简化,因为首先执行方法重载解析,最后应用类型推断(参见 JLS §15.12.2)。因此,类型推断可以提供的信息不能用于解决重载问题。

但请注意 15.12.2.1. Identify Potentially Applicable Methods 中描述的第一步包含:

An expression is potentially compatible with a target type according to the following rules:

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

  • A method reference expression (§15.13) is potentially compatible with a functional interface type if, where the type's function type arity is n, there exists at least one potentially applicable method for the method reference expression with arity n (§15.13.1), and one of the following is true:

  • The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is i) static and supports arity n, or ii) not static and supports arity n-1.

  • The method reference expression has some other form and at least one potentially applicable method is not static.

The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface target types. In some cases involving type argument inference, a lambda expression appearing as a method invocation argument cannot be properly typed until after overload resolution.

因此,在第一个示例中,其中一个方法是按 lambda 的形状排序的,而在方法引用或由唯一调用表达式组成的 lambda 表达式的情况下,两种可能适用的方法都会承受第一个选择过程并产生“类型推断之前出现的“模糊”错误,以帮助找到目标方法以确定它是 void 还是返回值的方法。

注意,就像使用 x->{ foo(); } 要使 lambda 表达式显式 void-兼容,您可以使用 x->( foo() ) 使 lambda 表达式显式值兼容。


您可以进一步阅读 this answer解释这种组合类型推断和方法重载解析的限制是一个深思熟虑(但不容易)的决定。

关于java - 使用 lambda 和泛型时对方法的引用不明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29323520/

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