java方法引用对于线程是不明确的。睡觉
我遇到了一个奇怪的问题,对Thread::sleep
的方法引用是不明确的,但具有相同签名的方法则不是
package test;
public class Test
{
public static void main(String[] args)
{
foo(Test::sleep, 1000L); //fine
foo((FooVoid<Long>)Thread::sleep, 1000L); //fine
foo(Thread::sleep, 1000L); //error
}
public static void sleep(long millis) throws InterruptedException
{
Thread.sleep(millis);
}
public static <P, R> void foo(Foo<P, R> function, P param) {}
public static <P> void foo(FooVoid<P> function, P param) {}
@FunctionalInterface
public interface Foo<P, R> {
R call(P param1) throws Exception;
}
@FunctionalInterface
public interface FooVoid<P> {
void call(P param1) throws Exception;
}
}
我有两个错误:
Error:(9, 17) java: reference to foo is ambiguous
both method <P,R>foo(test.Test.Foo<P,R>,P) in test.Test and method <P>foo(test.Test.FooVoid<P>,P) in test.Test match
Error:(9, 20) java: incompatible types: cannot infer type-variable(s) P,R
(argument mismatch; bad return type in method reference
void cannot be converted to R)
我看到的唯一区别是Thread::sleep
是native
。它改变了什么吗?我不认为超载在这里起作用。为什么会这样
编辑:使用javac版本1.8.0_111
# 1 楼答案
问题是,
Thread.sleep
和foo
都超载了。所以有一个循环依赖sleep
方法,我们需要知道目标类型,即要调用哪个foo
方法foo
方法,我们需要知道参数的函数签名,即我们选择了哪个sleep
方法虽然人类读者很清楚,在这种情况下,只有2×2组合中的一个是有效的,但编译器必须遵循适用于任意组合的正式规则,因此,语言设计者不得不做出让步
为了方法引用的有用性,对明确的引用有一种特殊的处理方法,比如你的
JLS §15.13.1Test::sleep
:注意,这种区别类似于隐式类型的lambda表达式(
arg -> expression
)和显式类型的lambda表达式((Type arg) -> expression
)之间的区别当您查看JLS, §15.12.2.5., Choosing the Most Specific Method时,您将看到方法引用的签名仅用于精确的方法引用,因为在选择正确的
foo
时,还没有决定正确的sleep
方法上述规则已在§15.12.2.5中说明。对于非泛型方法,重定向到泛型方法的§18.5.4(这里适用于
foo
方法是泛型的),包含exactly the same rule的措辞略有不同由于在选择最具体的方法时没有考虑方法引用的签名,因此没有最具体的方法,并且
foo
的调用是不明确的。第二个编译器错误是继续处理源代码并可能报告更多错误的策略的结果,而不是在第一个错误时立即停止编译。两次调用foo
中的一次调用导致了一个“不兼容类型”错误,如果该调用正在发生,但实际上,由于“不明确调用”错误,已经排除了这种可能性# 2 楼答案
您可以在自己的类中重新创建问题,方法是向类测试添加一个带有两个参数的方法
sleep
,如下所示:所以这个问题实际上是由方法sleep过载引起的
JLS指出,初始方法选择代码只查看函数接口的类型参数数量——只有在第二阶段,它才会查看函数接口内方法的签名
JLS 15.13:
(本节倒数第二段)
因此,在
Thread::sleep
的情况下,void sleep(long)
可能匹配功能接口FooVoid<P>
,而重载void sleep(long, int)
可能匹配功能接口Foo<P, R>
。这就是为什么会出现“对foo的引用不明确”错误当它试图进一步研究如何将
Foo<P, R>
与函数方法R call(P param1)
匹配到方法void sleep(long, int)
时,它发现这实际上是不可能的,并且会出现另一个编译错误:# 3 楼答案
我个人认为这是某种递归,某种程度上是这样的:我们需要解析方法才能找到目标类型,但我们需要知道目标类型才能解析方法。这与一个特殊的无效兼容性规则有关,但我承认我并不完全明白
当你有这样的事情时,事情就更有趣了:
并尝试通过以下方式拨打:
顺便说一句,如果你让你的lambda显式,这将起作用: