Answer to survey question
/* | |
* This program takes the code from the survey question, which uses | |
* CompletableFuture.whenComplete(BiConsumer) and compares it with | |
* analogous code using CompletableFuture.handle(BiFunction). The only | |
* difference is the absence of return values in the consumer. | |
* | |
* It runs both variants with all different combinations of conditions | |
* ("true" in the survey question code) to produce the following output: | |
* | |
* 1. handle: c1=false, c2=false, result is A | |
* 2. handle: c1=false, c2=true, result is A | |
* 3. handle: c1=true, c2=false, result is B | |
* 4. handle: c1=true, c2=true, exception is SecondException | |
* | |
* 5. whenComplete: c1=false, c2=false, result is A | |
* 6. whenComplete: c1=false, c2=true, result is A | |
* 7. whenComplete: c1=true, c2=false, exception is FirstException | |
* 8. whenComplete: c1=true, c2=true, exception is FirstException | |
* | |
* I interpret the survey question as being whether the exception | |
* in line 8 is (I) correct by analogy with line 7, or (II) incorrect | |
* by analogy with line 4. (In either case, you'd want the suppressed | |
* exception to be the other exception thrown.) | |
* | |
* I vote for the latter case (II), which would require SecondException | |
* thrown with FirstException suppressed. My argument is that the analogy | |
* with line 4 is stronger: There are no results involved, so the version | |
* using BiConsumer should behave like the version using BiFunction. | |
*/ | |
package net.peierls.example.wcsurvey; | |
import java.util.concurrent.CompletableFuture; | |
import java.util.concurrent.CompletionException; | |
public class WhenCompleteSurvey { | |
static class FirstException extends RuntimeException { | |
} | |
static class SecondException extends RuntimeException { | |
} | |
final boolean c1; | |
final boolean c2; | |
WhenCompleteSurvey(boolean c1, boolean c2) { | |
this.c1 = c1; | |
this.c2 = c2; | |
} | |
void handle(int i) { | |
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> { | |
if (c1) | |
throw new FirstException(); | |
else | |
return "A"; | |
}); | |
CompletableFuture<String> f2 = f1.handle((result, exception) -> { | |
if (exception != null) { | |
if (c2) | |
throw new SecondException(); | |
else | |
return "B"; | |
} | |
else | |
return result; | |
}); | |
try { | |
String result = f2.join(); | |
System.out.printf("%d. handle: c1=%s, c2=%s, result is %s%n", i, c1, c2, result); | |
} catch (CompletionException e) { | |
Throwable t = e.getCause(); | |
System.out.printf("%d. handle: c1=%s, c2=%s, exception is %s%n", i, c1, c2, t.getClass().getSimpleName()); | |
} | |
} | |
void whenComplete(int i) { | |
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> { | |
if (c1) | |
throw new FirstException(); | |
else | |
return "A"; | |
}); | |
CompletableFuture<String> f2 = f1.whenComplete((result, exception) -> { | |
if (exception != null) { | |
if (c2) | |
throw new SecondException(); | |
else | |
return; | |
} | |
else | |
return; | |
}); | |
try { | |
String result = f2.join(); | |
System.out.printf("%d. whenComplete: c1=%s, c2=%s, result is %s%n", i, c1, c2, result); | |
} catch (CompletionException e) { | |
Throwable t = e.getCause(); | |
System.out.printf("%d. whenComplete: c1=%s, c2=%s, exception is %s%n", i, c1, c2, t.getClass().getSimpleName()); | |
} | |
} | |
static void handle(int i, boolean c1, boolean c2) { | |
new WhenCompleteSurvey(c1, c2).handle(i); | |
} | |
static void whenComplete(int i, boolean c1, boolean c2) { | |
new WhenCompleteSurvey(c1, c2).whenComplete(i); | |
} | |
public static void main(String... args) throws InterruptedException { | |
handle(1, false, false); | |
handle(2, false, true); | |
handle(3, true, false); | |
handle(4, true, true); | |
System.out.println(); | |
whenComplete(5, false, false); | |
whenComplete(6, false, true); | |
whenComplete(7, true, false); | |
whenComplete(8, true, true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment