ski*_*iwi 23 java lambda java-8
我在下面的代码中收到错误,我认为不应该存在...使用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) {
}
}
Run Code Online (Sandbox Code Playgroud)
错误如下:
错误:(17,9)java:对consumerIntFunctionTest的引用与net.tuis.ubench.Ambiguous中的方法consumerIntFunctionTest(java.util.function.Consumer,java.util.function.IntFunction)和方法consumerIntFunctionTest(java.util.)都不明确. net.tuis.ubench.Ambiguous匹配中的function.Function,java.util.function.IntFunction)
错误发生在以下行:
consumerIntFunctionTest(Arrays::sort, int[]::new);
Run Code Online (Sandbox Code Playgroud)
我相信应该没有错误,因为所有Arrays::sort引用都是类型的void,并且它们都没有返回值.正如您所看到的,当我明确扩展lambda 时它确实有效Consumer<T>.
这真的是javac中的错误,还是JLS声明lambda在这种情况下无法自动扩展?如果是后者,我仍然认为这很奇怪,consumerIntFunctionTest因为第一个参数Function<T, ?>不应该匹配.
在你的第一个例子中
consumerIntFunctionTest(data -> {
Arrays.sort(data);
}, int[]::new);
Run Code Online (Sandbox Code Playgroud)
lambda表达式具有一个void兼容的块,可以通过表达式的结构来识别,而无需解析实际的类型.
相比之下,在这个例子中
consumerIntFunctionTest(Arrays::sort, int[]::new);
Run Code Online (Sandbox Code Playgroud)
必须解析方法引用,以确定它是否符合void函数(Consumer)或值返回函数(Function).这同样适用于简化的lambda表达式
consumerIntFunctionTest(data -> Arrays.sort(data), int[]::new);
Run Code Online (Sandbox Code Playgroud)
void根据已解决的目标方法,它可以是兼容的或价值兼容的.
问题是解析方法需要有关所需签名的知识,这应该通过目标类型确定,但是在知道通用方法的类型参数之前,目标类型是未知的.虽然理论上两者都可以立即确定,但是在规范中简化了(仍然非常复杂)过程,该方法首先执行重载决策,最后应用类型推断(参见JLS§15.12.2).因此,类型推断可以提供的信息不能用于解决重载决策.
但请注意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节).
方法引用表达式(第15.13节)可能与函数接口类型兼容,如果类型的函数类型arity为n,则存在至少一个可能适用于方法引用表达式的方法,其中arity为n(§15.13.1),并且以下之一是真的:
方法引用表达式具有ReferenceType :: [TypeArguments]标识符的形式,并且至少一个可能适用的方法是i)static并支持arity n,或ii)not static并支持arity n-1.
方法引用表达式具有一些其他形式,并且至少一个可能适用的方法不是静态的.
...
潜在适用性的定义超出了基本的arity检查,也考虑了功能接口目标类型的存在和"形状".在某些涉及类型参数推断的情况下,在重载解析之前,无法正确键入作为方法调用参数出现的lambda表达式.
因此,在第一个示例中,其中一个方法按照lambda的形状进行排序,而在方法引用或lambda表达式由单个调用表达式组成的情况下,两个可能适用的方法都会忍受第一个选择过程并产生"模糊"错误在类型推断之前可以启动以帮助找到目标方法以确定它是否是void返回方法.
请注意,与使用x->{ foo(); }显式void兼容的lambda表达式一样,您可以使用x->( foo() )使lambda表达式显式值兼容.
你疯了进一步阅读这个答案,解释说这种组合类型推断和方法重载决策的限制是一个刻意(但不容易)的决定.