Ours avec moi, l'introduction est un peu longue haleine, mais c'est un puzzle intéressant.
J'ai ce code:
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(); }
}
}
}
Je suis en train d'ajouter des tâches sur une file d'attente et de les exécuter dans l'ordre. Je m'attendais à tous les 3 cas d'invoquer l' add(Runnable)
méthode; cependant, ce qui se passe réellement est que le cas 2 est interprété comme un Supplier<CompletionStage<Void>>
qui lève une exception avant de retourner un CompletionStage
afin de les "cela ne devrait jamais se produire" bloc de code est déclenchée et le 3 n'est jamais à court.
J'ai confirmé que le cas 2 est en invoquant la mauvaise méthode en parcourant le code à l'aide d'un débogueur.
Pourquoi n'est-ce pas l' Runnable
méthode de prise en invoquée pour le deuxième cas?
Apparemment, ce problème se produit uniquement sur Java 10 ou plus, alors assurez-vous de tester sous cet environnement.
Mise à JOUR: Selon JLS §15.12.2.1. Identifier Potentiellement Méthodes Applicables et plus particulièrement JLS §15.27.2. Corps de Lambda, il semble qu' () -> { throw new RuntimeException(); }
relèvent de la catégorie de "vide-compatible" et de la "valeur-compatible". Donc clairement il y a une certaine ambiguïté dans ce cas, mais je ne comprends pas pourquoi, Supplier
est plus approprié à une surcharge de Runnable
ici. C'est pas comme si l'ancien jette les exceptions que les seconds ne le sont pas.
Je ne comprends pas assez sur la spécification de dire ce qui doit se passer dans ce cas.
J'ai déposé un rapport de bug qui est visible https://bugs.openjdk.java.net/browse/JDK-8208490