为何assertThrows无歧义,assertDoesNotThrow报get方法引用歧义?
为什么JUnit5中assertThrows可正常使用futureTask::get,而assertDoesNotThrow却报方法引用歧义?
这问题的核心在于Java方法引用的重载解析规则,以及JUnit5中assertThrows和assertDoesNotThrow的方法重载差异:
1. 先看assertThrows为什么没问题
assertThrows的第二个参数明确是Executable类型,这个函数式接口的方法签名是void execute() throws Throwable(无参、无返回值)。
当你传入futureTask::get时,编译器会严格按照Executable的要求匹配方法:
FutureTask的get(long, TimeUnit)需要两个参数,完全无法适配无参的execute(),直接被排除;get()是无参方法,虽然它有返回值,但Java允许将有返回值的方法适配到无返回值的函数式接口(返回值会被自动忽略),且抛出的异常也符合Throwable的要求,所以唯一匹配的就是这个无参get(),自然没有歧义。
2. assertDoesNotThrow报错的原因
assertDoesNotThrow有两个关键重载:
void assertDoesNotThrow(Executable executable)<T> T assertDoesNotThrow(Supplier<T> supplier)
当你直接传入futureTask::get时,编译器无法确定你要适配哪个重载:
- 它可以适配
Executable(忽略get()的返回值); - 也可以适配
Supplier<String>(使用get()的返回值)。
这种目标类型的不确定性,导致编译器在解析方法引用时出现歧义——虽然get(long, TimeUnit)根本无法适配这两个接口,但编译器的错误提示会简化表述为“get的两个重载都匹配”,本质是assertDoesNotThrow的重载让目标类型不明确。
解决方法
消除歧义的方式很简单,明确告诉编译器你要适配的目标类型:
- 方式一:强制类型转换
assertDoesNotThrow((Executable) futureTask::get);
- 方式二:用lambda代替方法引用
assertDoesNotThrow(() -> futureTask.get());
内容的提问来源于stack exchange,提问作者Panicking Developer
相关产品推荐
相关产品推荐

