为什么 lambda 更改在引发运行时异常时会重载?
忍受我,介绍有点冗长,但这是一个有趣的难题。
我有这个代码:
public class Testcase {
public static void main(String[] args){
EventQueue queue = new EventQueue();
queue.add(() -> System.out.println("case1"));
queue.add(() -> {
System.out.println("case2");
throw new IllegalArgumentException("case2-exception");});
queue.runNextTask();
queue.add(() -> System.out.println("case3-never-runs"));
}
private static class EventQueue {
private final Queue<Supplier<CompletionStage<Void>>> queue = new ConcurrentLinkedQueue<>();
public void add(Runnable task) {
queue.add(() -> CompletableFuture.runAsync(task));
}
public void add(Supplier<CompletionStage<Void>> task) {
queue.add(task);
}
public void runNextTask() {
Supplier<CompletionStage<Void>> task = queue.poll();
if (task == null)
return;
try {
task.get().
whenCompleteAsync((value, exception) -> runNextTask()).
exceptionally(exception -> {
exception.printStackTrace();
return null; });
}
catch (Throwable exception) {
System.err.println("This should never happen...");
exception.printStackTrace(); }
}
}
}
我正在尝试将任务添加到队列中并按顺序运行它们。我期望所有3个案例都调用该方法;但是,实际发生的情况是,情况 2 被解释为在返回 a 之前引发异常的 a,因此触发“这不应该发生”代码块,并且情况 3 永远不会运行。add(Runnable)
Supplier<CompletionStage<Void>>
CompletionStage
我确认情况 2 通过使用调试器单步执行代码来调用错误的方法。
为什么没有为第二种情况调用 Runnable
方法?
显然,此问题仅发生在Java 10或更高版本上,因此请务必在此环境中进行测试。
更新:根据 JLS §15.12.2.1。确定可能适用的方法,更具体地说是JLS §15.27.2。Lambda Body似乎属于“void-compatible”和“value-compatible”的范畴。很明显,在这种情况下存在一些歧义,但我当然不明白为什么比这里更适合重载。这并不是说前者会引发任何后者没有的异常。() -> { throw new RuntimeException(); }
Supplier
Runnable
我对规范的了解还不够多,无法说明在这种情况下应该发生什么。
我提交了一个错误报告,该报告在 https://bugs.openjdk.java.net/browse/JDK-8208490