From 425c5bee2b7c322b9ee7c39886d7f0cb9b525a9e Mon Sep 17 00:00:00 2001 From: Daniel Dietrich Date: Sun, 7 May 2017 00:55:35 +0200 Subject: [PATCH] Removed Fatal and NonFatal exception (#1976) --- vavr-test/generator/Generator.scala | 56 +++--- .../main/java/io/vavr/test/Property.java | 180 ++++++++++-------- vavr/src/main/java/io/vavr/Lazy.java | 2 +- vavr/src/main/java/io/vavr/Value.java | 30 +-- .../java/io/vavr/collection/Traversable.java | 6 + .../main/java/io/vavr/concurrent/Future.java | 23 ++- .../src/main/java/io/vavr/control/Either.java | 14 +- .../src/main/java/io/vavr/control/Option.java | 6 + vavr/src/main/java/io/vavr/control/Try.java | 179 +++++------------ .../main/java/io/vavr/control/Validation.java | 6 +- .../java/io/vavr/control/package-info.java | 5 - .../test/java/io/vavr/AbstractValueTest.java | 2 +- .../java/io/vavr/concurrent/FutureTest.java | 16 +- .../test/java/io/vavr/control/TryTest.java | 110 +++++------ 14 files changed, 297 insertions(+), 338 deletions(-) diff --git a/vavr-test/generator/Generator.scala b/vavr-test/generator/Generator.scala index f4212946d6..8e9ac761ec 100644 --- a/vavr-test/generator/Generator.scala +++ b/vavr-test/generator/Generator.scala @@ -93,37 +93,37 @@ def generateMainClasses(): Unit = { } /** - * Creates an Error caused by an exception when obtaining a generator. + * Creates a CheckError caused by an exception when obtaining a generator. * * @param position The position of the argument within the argument list of the property, starting with 1. * @param size The size hint passed to the {@linkplain Arbitrary} which caused the error. * @param cause The error which occurred when the {@linkplain Arbitrary} tried to obtain the generator {@linkplain Gen}. - * @return a new Error instance. + * @return a new CheckError instance. */ - private static Error arbitraryError(int position, int size, Throwable cause) { - return new Error(String.format("Arbitrary %s of size %s: %s", position, size, cause.getMessage()), cause); + private static CheckError arbitraryError(int position, int size, Throwable cause) { + return new CheckError(String.format("Arbitrary %s of size %s: %s", position, size, cause.getMessage()), cause); } /** - * Creates an Error caused by an exception when generating a value. + * Creates a CheckError caused by an exception when generating a value. * * @param position The position of the argument within the argument list of the property, starting with 1. * @param size The size hint of the arbitrary which called the generator {@linkplain Gen} which caused the error. * @param cause The error which occurred when the {@linkplain Gen} tried to generate a random value. - * @return a new Error instance. + * @return a new CheckError instance. */ - private static Error genError(int position, int size, Throwable cause) { - return new Error(String.format("Gen %s of size %s: %s", position, size, cause.getMessage()), cause); + private static CheckError genError(int position, int size, Throwable cause) { + return new CheckError(String.format("Gen %s of size %s: %s", position, size, cause.getMessage()), cause); } /** - * Creates an Error caused by an exception when testing a Predicate. + * Creates a CheckError caused by an exception when testing a Predicate. * * @param cause The error which occurred when applying the {@linkplain java.util.function.Predicate}. - * @return a new Error instance. + * @return a new CheckError instance. */ - private static Error predicateError(Throwable cause) { - return new Error("Applying predicate: " + cause.getMessage(), cause); + private static CheckError predicateError(Throwable cause) { + return new CheckError("Applying predicate: " + cause.getMessage(), cause); } ${(1 to N).gen(i => { @@ -189,7 +189,7 @@ def generateMainClasses(): Unit = { val optionType = im.getType("io.vavr.control.Option") val randomType = im.getType("java.util.Random") val tryType = im.getType("io.vavr.control.Try") - val nonFatalType = "Try.NonFatalException" + val checkException = "CheckException" val tupleType = im.getType(s"io.vavr.Tuple") val generics = (1 to i).gen(j => s"T$j")(", ") @@ -262,20 +262,20 @@ def generateMainClasses(): Unit = { return new CheckResult.Falsified(name, i, $tupleType.of(${(1 to i).gen(j => s"val$j")(", ")})); } } - } catch($nonFatalType nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), $optionType.some($tupleType.of(${(1 to i).gen(j => s"val$j")(", ")}))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, $optionType.some($tupleType.of(${(1 to i).gen(j => s"val$j")(", ")}))); } - } catch($nonFatalType nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), $optionType.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, $optionType.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch($nonFatalType nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), $optionType.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, $optionType.none()); } } } @@ -302,6 +302,18 @@ def generateMainClasses(): Unit = { return precondition && !postcondition; } } + + /** + * Internally used to provide more specific error messages. + */ + static class CheckError extends Error { + + private static final long serialVersionUID = 1L; + + CheckError(String message, Throwable cause) { + super(message, cause); + } + } } """ } diff --git a/vavr-test/src-gen/main/java/io/vavr/test/Property.java b/vavr-test/src-gen/main/java/io/vavr/test/Property.java index 1e578f6b13..b028179f51 100644 --- a/vavr-test/src-gen/main/java/io/vavr/test/Property.java +++ b/vavr-test/src-gen/main/java/io/vavr/test/Property.java @@ -67,37 +67,37 @@ private static void log(String msg) { } /** - * Creates an Error caused by an exception when obtaining a generator. + * Creates a CheckError caused by an exception when obtaining a generator. * * @param position The position of the argument within the argument list of the property, starting with 1. * @param size The size hint passed to the {@linkplain Arbitrary} which caused the error. * @param cause The error which occurred when the {@linkplain Arbitrary} tried to obtain the generator {@linkplain Gen}. - * @return a new Error instance. + * @return a new CheckError instance. */ - private static Error arbitraryError(int position, int size, Throwable cause) { - return new Error(String.format("Arbitrary %s of size %s: %s", position, size, cause.getMessage()), cause); + private static CheckError arbitraryError(int position, int size, Throwable cause) { + return new CheckError(String.format("Arbitrary %s of size %s: %s", position, size, cause.getMessage()), cause); } /** - * Creates an Error caused by an exception when generating a value. + * Creates a CheckError caused by an exception when generating a value. * * @param position The position of the argument within the argument list of the property, starting with 1. * @param size The size hint of the arbitrary which called the generator {@linkplain Gen} which caused the error. * @param cause The error which occurred when the {@linkplain Gen} tried to generate a random value. - * @return a new Error instance. + * @return a new CheckError instance. */ - private static Error genError(int position, int size, Throwable cause) { - return new Error(String.format("Gen %s of size %s: %s", position, size, cause.getMessage()), cause); + private static CheckError genError(int position, int size, Throwable cause) { + return new CheckError(String.format("Gen %s of size %s: %s", position, size, cause.getMessage()), cause); } /** - * Creates an Error caused by an exception when testing a Predicate. + * Creates a CheckError caused by an exception when testing a Predicate. * * @param cause The error which occurred when applying the {@linkplain java.util.function.Predicate}. - * @return a new Error instance. + * @return a new CheckError instance. */ - private static Error predicateError(Throwable cause) { - return new Error("Applying predicate: " + cause.getMessage(), cause); + private static CheckError predicateError(Throwable cause) { + return new CheckError("Applying predicate: " + cause.getMessage(), cause); } /** @@ -609,20 +609,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -688,20 +688,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1, val2)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1, val2))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -771,20 +771,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1, val2, val3))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -858,20 +858,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1, val2, val3, val4))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -949,20 +949,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1, val2, val3, val4, val5))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -1044,20 +1044,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5, val6)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5, val6))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1, val2, val3, val4, val5, val6))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -1143,20 +1143,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5, val6, val7)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5, val6, val7))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1, val2, val3, val4, val5, val6, val7))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -1246,20 +1246,20 @@ public CheckResult check(Random random, int size, int tries) { return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5, val6, val7, val8)); } } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5, val6, val7, val8))); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.some(Tuple.of(val1, val2, val3, val4, val5, val6, val7, val8))); } - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, i, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, i, err, Option.none()); } } logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted); return new CheckResult.Satisfied(name, tries, exhausted); - } catch(Try.NonFatalException nonFatal) { - logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage()); - return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none()); + } catch(CheckError err) { + logErroneous(name, 0, System.currentTimeMillis() - startTime, err.getMessage()); + return new CheckResult.Erroneous(name, 0, err, Option.none()); } } } @@ -1284,4 +1284,16 @@ boolean isFalse() { return precondition && !postcondition; } } + + /** + * Internally used to provide more specific error messages. + */ + static class CheckError extends Error { + + private static final long serialVersionUID = 1L; + + CheckError(String message, Throwable cause) { + super(message, cause); + } + } } \ No newline at end of file diff --git a/vavr/src/main/java/io/vavr/Lazy.java b/vavr/src/main/java/io/vavr/Lazy.java index f4e85f8437..758388fdb9 100644 --- a/vavr/src/main/java/io/vavr/Lazy.java +++ b/vavr/src/main/java/io/vavr/Lazy.java @@ -136,12 +136,12 @@ public Option filter(Predicate predicate) { * On subsequent calls, returns the cached value. * * @return the lazy evaluated value - * @throws NoSuchElementException if this value is undefined */ @Override public T get() { return (supplier == null) ? value : computeValue(); } + private synchronized T computeValue() { final Supplier s = supplier; if (s != null) { diff --git a/vavr/src/main/java/io/vavr/Value.java b/vavr/src/main/java/io/vavr/Value.java index d705a48d69..99c96989e6 100644 --- a/vavr/src/main/java/io/vavr/Value.java +++ b/vavr/src/main/java/io/vavr/Value.java @@ -319,9 +319,20 @@ default void forEach(Consumer action) { /** * Gets the underlying value or throws if no value is present. + *

+ * IMPORTANT! This method will throw an undeclared {@link Throwable} if {@code isEmpty() == true} is true. + *

+ * Because the 'empty' state indicates that there is no value present that can be returned, + * {@code get()} has to throw in such a case. Generally, implementing classes should throw a + * {@link java.util.NoSuchElementException} if {@code isEmpty()} returns true. + *

+ * However, there exist use-cases, where implementations may throw other exceptions. See {@link Try#get()}. + *

+ * Additional note: Dynamic proxies will wrap an undeclared exception in a {@link java.lang.reflect.UndeclaredThrowableException}. + *

+ * Hint: A safe variant of {@code get()} is {@link #getOption()}. * - * @return the underlying value - * @throws java.util.NoSuchElementException if no value is defined + * @return the underlying value if this is not empty, otherwise {@code get()} throws a {@code Throwable} */ T get(); @@ -379,8 +390,7 @@ default T getOrElseThrow(Supplier supplier) throws X { * * @param supplier An alternative value supplier. * @return A value of type {@code T}. - * @throws NullPointerException if supplier is null - * @throws Try.NonFatalException containing the original exception if this Value was empty and the Try failed. + * @throws NullPointerException if supplier is null */ default T getOrElseTry(CheckedFunction0 supplier) { Objects.requireNonNull(supplier, "supplier is null"); @@ -555,15 +565,9 @@ default CharSeq toCharSeq() { @GwtIncompatible default CompletableFuture toCompletableFuture() { final CompletableFuture completableFuture = new CompletableFuture<>(); - try { - completableFuture.complete(get()); - } catch (Try.FatalException x) { - throw x; - } catch (Try.NonFatalException x) { - completableFuture.completeExceptionally(x.getCause()); - } catch (Throwable x) { - completableFuture.completeExceptionally(x); - } + Try.of(this::get) + .onSuccess(completableFuture::complete) + .onFailure(completableFuture::completeExceptionally); return completableFuture; } diff --git a/vavr/src/main/java/io/vavr/collection/Traversable.java b/vavr/src/main/java/io/vavr/collection/Traversable.java index 19184954ab..d60fed2dc5 100644 --- a/vavr/src/main/java/io/vavr/collection/Traversable.java +++ b/vavr/src/main/java/io/vavr/collection/Traversable.java @@ -494,6 +494,12 @@ default U foldLeft(U zero, BiFunction f) @Override U foldRight(U zero, BiFunction f); + /** + * Gets the first value in iteration order if this {@code Traversable} is not empty, otherwise throws. + * + * @return the first value + * @throws NoSuchElementException if this {@code Traversable} is empty. + */ @Override default T get() { return iterator().next(); diff --git a/vavr/src/main/java/io/vavr/concurrent/Future.java b/vavr/src/main/java/io/vavr/concurrent/Future.java index dc44caaa32..44b779a3e1 100644 --- a/vavr/src/main/java/io/vavr/concurrent/Future.java +++ b/vavr/src/main/java/io/vavr/concurrent/Future.java @@ -1024,21 +1024,20 @@ default void forEach(Consumer action) { } /** - * Returns the value of the future. Waits for the result if necessary. - * - * @return The value of this future. - * @throws NoSuchElementException if the computation unexpectedly failed or was interrupted. + * Gets the value if the computation result is a {@code Success} or throws if it was a {@code Failure}. + * Waits for the result if necessary by blocking the current thread. + *

+ * IMPORTANT! If the computation result is a {@link Try.Failure}, the underlying {@code cause} of type {@link Throwable} is thrown. + * + * @return The value of this {@code Future}. */ - // DEV-NOTE: A NoSuchElementException is thrown instead of the exception of the underlying Failure in order to - // be conform to Value#get @Override default T get() { - // is empty will block until result is available - if (isEmpty()) { - throw new NoSuchElementException("get on failed future"); - } else { - return getValue().get().get(); + // does not need to be synchronized, await() has to check the completed state again + if (!isCompleted()) { + await(); } + return getValue().get().get(); } /** @@ -1058,7 +1057,7 @@ default boolean isAsync() { */ @Override default boolean isEmpty() { - // does not need to be synchronized, wait() has to check the completed state again + // does not need to be synchronized, await() has to check the completed state again if (!isCompleted()) { await(); } diff --git a/vavr/src/main/java/io/vavr/control/Either.java b/vavr/src/main/java/io/vavr/control/Either.java index 90fea70b97..602ea45910 100644 --- a/vavr/src/main/java/io/vavr/control/Either.java +++ b/vavr/src/main/java/io/vavr/control/Either.java @@ -325,7 +325,7 @@ default Option> filter(Predicate predicate) { * Gets the right value if this is a {@code Right} or throws if this is a {@code Left}. * * @return the right value - * @throws NoSuchElementException if this is a Left. + * @throws NoSuchElementException if this is a {@code Left}. */ @Override R get(); @@ -470,10 +470,10 @@ public boolean isSingleValued() { } /** - * Gets the Left value or throws. + * Gets the {@code Left} value or throws. * - * @return the left value, if the underlying Either is a Left - * @throws NoSuchElementException if the underlying either of this LeftProjection is a Right + * @return the left value, if the underlying {@code Either} is a {@code Left} + * @throws NoSuchElementException if the underlying {@code Either} of this {@code LeftProjection} is a {@code Right} */ @Override public L get() { @@ -723,10 +723,10 @@ public boolean isSingleValued() { } /** - * Gets the Right value or throws. + * Gets the {@code Right} value or throws. * - * @return the left value, if the underlying Either is a Right - * @throws NoSuchElementException if the underlying either of this RightProjection is a Left + * @return the right value, if the underlying {@code Either} is a {@code Right} + * @throws NoSuchElementException if the underlying {@code Either} of this {@code RightProjection} is a {@code Left} */ @Override public R get() { diff --git a/vavr/src/main/java/io/vavr/control/Option.java b/vavr/src/main/java/io/vavr/control/Option.java index 0d236a31d1..5ed81dbef9 100644 --- a/vavr/src/main/java/io/vavr/control/Option.java +++ b/vavr/src/main/java/io/vavr/control/Option.java @@ -254,6 +254,12 @@ default boolean isSingleValued() { return true; } + /** + * Gets the value if this is a {@code Some} or throws if this is a {@code None}. + * + * @return the value + * @throws NoSuchElementException if this is a {@code None}. + */ @Override T get(); diff --git a/vavr/src/main/java/io/vavr/control/Try.java b/vavr/src/main/java/io/vavr/control/Try.java index b8ae220ae8..c447f55c06 100644 --- a/vavr/src/main/java/io/vavr/control/Try.java +++ b/vavr/src/main/java/io/vavr/control/Try.java @@ -22,9 +22,24 @@ import java.util.function.Supplier; import static io.vavr.API.Match; +import static io.vavr.control.TryModule.isFatal; +import static io.vavr.control.TryModule.sneakyThrow; /** - * An implementation similar to Scala's Try control. + * The Try control gives us the ability write safe code without focusing on try-catch blocks in the presence of exceptions. + *

+ * The following exceptions are considered to be fatal/non-recoverable: + *

+ *

+ * Important note: Try may re-throw (undeclared) exceptions, e.g. on {@code get()}. From within a + * dynamic proxy {@link java.lang.reflect.InvocationHandler} this will lead to an + * {@link java.lang.reflect.UndeclaredThrowableException}. For more information, please read + * Dynamic Proxy Classes. * * @param Value type in the case of success. * @author Daniel Dietrich @@ -449,10 +464,13 @@ default Try flatMapTry(CheckedFunction1 + * IMPORTANT! If this is a {@link Failure}, the underlying {@code cause} of type {@link Throwable} is thrown. + *

+ * The thrown exception is exactly the same as the result of {@link #getCause()}. * - * @return The result of this Try. - * @throws NonFatalException if this is a Failure + * @return The result of this {@code Try}. */ @Override T get(); @@ -935,29 +953,31 @@ final class Failure implements Try, Serializable { private static final long serialVersionUID = 1L; - private final NonFatalException cause; + private final Throwable cause; /** * Constructs a Failure. * - * @param exception A cause of type Throwable, may not be null. - * @throws NullPointerException if exception is null - * @throws Error if the given exception if fatal, i.e. non-recoverable + * @param cause A cause of type Throwable, may not be null. + * @throws NullPointerException if {@code cause} is null + * @throws Throwable if the given {@code cause} is fatal, i.e. non-recoverable */ - private Failure(Throwable exception) { - Objects.requireNonNull(exception, "exception is null"); - cause = NonFatalException.of(exception); + private Failure(Throwable cause) { + Objects.requireNonNull(cause, "cause is null"); + if (isFatal(cause)) { + sneakyThrow(cause); + } + this.cause = cause; } - // Throws NonFatal instead of Throwable because it is a RuntimeException which does not need to be checked. @Override - public T get() throws NonFatalException { - throw cause; + public T get() { + return sneakyThrow(cause); } @Override public Throwable getCause() { - return cause.getCause(); + return cause; } @Override @@ -977,7 +997,7 @@ public boolean isSuccess() { @Override public boolean equals(Object obj) { - return (obj == this) || (obj instanceof Failure && Objects.equals(cause, ((Failure) obj).cause)); + return (obj == this) || (obj instanceof Failure && Arrays.deepEquals(cause.getStackTrace(), ((Failure) obj).cause.getStackTrace())); } @Override @@ -987,12 +1007,12 @@ public String stringPrefix() { @Override public int hashCode() { - return Objects.hashCode(cause.getCause()); + return Arrays.hashCode(cause.getStackTrace()); } @Override public String toString() { - return stringPrefix() + "(" + cause.getCause() + ")"; + return stringPrefix() + "(" + cause + ")"; } } @@ -1466,120 +1486,21 @@ public Try of(CheckedFunction8 - * See {@link NonFatalException}. - * @deprecated Will be removed in 0.9.0. Instead we throw sneaky. - */ - @Deprecated - final class FatalException extends RuntimeException implements Serializable { - - private static final long serialVersionUID = 1L; - - private FatalException(Throwable exception) { - super(exception); - } - - /** - * Two Fatal exceptions are equal, if they have the same stack trace. - * - * @param o An object - * @return true, if o equals this, false otherwise. - */ - @Override - public boolean equals(Object o) { - return (o == this) || (o instanceof FatalException - && Arrays.deepEquals(getCause().getStackTrace(), ((FatalException) o).getCause().getStackTrace())); - } - - @Override - public int hashCode() { - return Objects.hashCode(getCause()); - } +interface TryModule { - @Override - public String toString() { - return "Fatal(" + getCause() + ")"; - } + static boolean isFatal(Throwable throwable) { + return throwable instanceof InterruptedException + || throwable instanceof LinkageError + || throwable instanceof ThreadDeath + || throwable instanceof VirtualMachineError; } - /** - * An unchecked wrapper for non-fatal/recoverable exceptions. The underlying exception can - * be accessed via {@link #getCause()}. - *

- * The following exceptions are considered to be fatal/non-recoverable: - *

    - *
  • {@linkplain InterruptedException}
  • - *
  • {@linkplain LinkageError}
  • - *
  • {@linkplain ThreadDeath}
  • - *
  • {@linkplain VirtualMachineError} (i.e. {@linkplain OutOfMemoryError} or {@linkplain StackOverflowError})
  • - *
- * @deprecated Will be removed in 0.9.0. Instead we throw sneaky. - */ - @Deprecated - final class NonFatalException extends RuntimeException implements Serializable { - - private static final long serialVersionUID = 1L; - - private NonFatalException(Throwable exception) { - super(exception); - } - - /** - * Wraps the given exception in a {@code NonFatal} or throws an {@link Error} if the given exception is fatal. - *

- * Note: InterruptedException is not considered to be fatal. It should be handled explicitly but we cannot - * throw it directly because it is not an Error. If we would wrap it in an Error, we couldn't handle it - * directly. Therefore it is not thrown as fatal exception. - * - * @param exception A Throwable - * @return A new {@code NonFatal} if the given exception is recoverable - * @throws Error if the given exception is fatal, i.e. not recoverable - * @throws NullPointerException if exception is null - */ - static NonFatalException of(Throwable exception) { - Objects.requireNonNull(exception, "exception is null"); - if (exception instanceof NonFatalException) { - return (NonFatalException) exception; - } else if (exception instanceof FatalException) { - throw (FatalException) exception; - } else { - final boolean isFatal = exception instanceof InterruptedException - || exception instanceof LinkageError - || exception instanceof ThreadDeath - || exception instanceof VirtualMachineError; - if (isFatal) { - throw new FatalException(exception); - } else { - return new NonFatalException(exception); - } - } - } - - /** - * Two NonFatal exceptions are equal, if they have the same stack trace. - * - * @param o An object - * @return true, if o equals this, false otherwise. - */ - @Override - public boolean equals(Object o) { - return (o == this) || (o instanceof NonFatalException - && Arrays.deepEquals(getCause().getStackTrace(), ((NonFatalException) o).getCause().getStackTrace())); - } - - @Override - public int hashCode() { - return Objects.hashCode(getCause()); - } - - @Override - public String toString() { - return "NonFatal(" + getCause() + ")"; - } + // DEV-NOTE: we do not plan to expose this as public API + @SuppressWarnings("unchecked") + static R sneakyThrow(Throwable t) throws T { + throw (T) t; } + } diff --git a/vavr/src/main/java/io/vavr/control/Validation.java b/vavr/src/main/java/io/vavr/control/Validation.java index 6e71aeeda6..d64b58dbe6 100644 --- a/vavr/src/main/java/io/vavr/control/Validation.java +++ b/vavr/src/main/java/io/vavr/control/Validation.java @@ -371,10 +371,10 @@ default boolean isEmpty() { } /** - * Gets the value of this Validation if is a Valid or throws if this is an Invalid + * Gets the value of this {@code Validation} if is a {@code Valid} or throws if this is an {@code Invalid}. * - * @return The value of this Validation - * @throws NoSuchElementException if this is an Invalid + * @return The value of this {@code Validation} + * @throws NoSuchElementException if this is an {@code Invalid} */ @Override T get(); diff --git a/vavr/src/main/java/io/vavr/control/package-info.java b/vavr/src/main/java/io/vavr/control/package-info.java index f9fc33fbd7..5a2af302d5 100644 --- a/vavr/src/main/java/io/vavr/control/package-info.java +++ b/vavr/src/main/java/io/vavr/control/package-info.java @@ -22,10 +22,5 @@ * Exceptions are handled with the {@linkplain io.vavr.control.Try} control which is either a * {@linkplain io.vavr.control.Try.Success}, containing a result, or a {@linkplain io.vavr.control.Try.Failure}, * containing an Exception. - *

- * Try internally handles exceptions by wrapping exceptions in a Cause. - * A Cause is unchecked, i.e. a RuntimeException, and is Fatal or NonFatal. - * Fatal exceptions cannot be handled and are thrown without further processing. - * NonFatal exceptions are wrapped in a Failure. */ package io.vavr.control; diff --git a/vavr/src/test/java/io/vavr/AbstractValueTest.java b/vavr/src/test/java/io/vavr/AbstractValueTest.java index cd97dccc31..4d08dc909b 100644 --- a/vavr/src/test/java/io/vavr/AbstractValueTest.java +++ b/vavr/src/test/java/io/vavr/AbstractValueTest.java @@ -164,7 +164,7 @@ public void shouldReturnAlternateValueWhenCallingGetOrElseTryOnEmptyValue() { assertThat(empty().getOrElseTry(() -> 2)).isEqualTo(2); } - @Test(expected = Try.NonFatalException.class) + @Test(expected = Error.class) public void shouldThrowWhenCallingGetOrElseTryOnEmptyValueAndTryIsAFailure() { empty().getOrElseTry(() -> { throw new Error(); diff --git a/vavr/src/test/java/io/vavr/concurrent/FutureTest.java b/vavr/src/test/java/io/vavr/concurrent/FutureTest.java index 3a62b760fb..e9fc339150 100644 --- a/vavr/src/test/java/io/vavr/concurrent/FutureTest.java +++ b/vavr/src/test/java/io/vavr/concurrent/FutureTest.java @@ -50,7 +50,7 @@ public IterableAssert isEqualTo(Object expected) { @Override protected Future empty() { - return Future.failed(trivialExecutorService(), new Error()); + return Future.failed(trivialExecutorService(), new NoSuchElementException()); } @Override @@ -77,7 +77,7 @@ protected int getPeekNonNilPerformingAnAction() { // -- static failed() @Test - public void shouldCreateFailerFuture() { + public void shouldCreateFailureThatFailsWithRuntimeException() { final Future failed = Future.failed(new RuntimeException("ooops")); waitUntil(failed::isCompleted); assertThat(failed.isFailure()).isTrue(); @@ -86,6 +86,16 @@ public void shouldCreateFailerFuture() { assertThat(t.getMessage()).isEqualTo("ooops"); } + @Test + public void shouldCreateFailureThatFailsWithError() { + final Future failed = Future.failed(new Error("ooops")); + waitUntil(failed::isCompleted); + assertThat(failed.isFailure()).isTrue(); + final Throwable t = failed.getValue().get().getCause(); + assertThat(t.getClass()).isEqualTo(Error.class); + assertThat(t.getMessage()).isEqualTo("ooops"); + } + @Test public void shouldCreateAndFailAFutureUsingForkJoinPool() { final Future future = Future.of(() -> { @@ -648,7 +658,7 @@ public void shouldInterruptLockedFuture() { assertCancelled(future); } - @Test(expected = NoSuchElementException.class) + @Test(expected = CancellationException.class) public void shouldThrowOnGetAfterCancellation() { final Future future = Future.of(Concurrent::waitForever); final boolean isCancelled = future.cancel(); diff --git a/vavr/src/test/java/io/vavr/control/TryTest.java b/vavr/src/test/java/io/vavr/control/TryTest.java index 6220feb247..1647135ac2 100644 --- a/vavr/src/test/java/io/vavr/control/TryTest.java +++ b/vavr/src/test/java/io/vavr/control/TryTest.java @@ -19,6 +19,8 @@ import org.junit.Test; import java.io.IOException; +import java.lang.reflect.Proxy; +import java.lang.reflect.UndeclaredThrowableException; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -60,7 +62,7 @@ protected int getPeekNonNilPerformingAnAction() { } @Override - @Test(expected = Try.NonFatalException.class) + @Test(expected = NoSuchElementException.class) public void shouldGetEmpty() { empty().get(); } @@ -594,15 +596,20 @@ public void shouldCreateFailureTryWithResources8() { // -- Failure.Cause - @Test(expected = Try.FatalException.class) - public void shouldDetectFatalException() throws Exception { - Try.NonFatalException.of(new OutOfMemoryError()); + @Test(expected = InterruptedException.class) + public void shouldRethrowInterruptedException() throws Exception { + Try.failure(new InterruptedException()); + } + + @Test(expected = OutOfMemoryError.class) + public void shouldRethrowOutOfMemoryError() throws Exception { + Try.failure(new OutOfMemoryError()); } @Test public void shouldDetectNonFatalException() throws Exception { - final Try.NonFatalException cause = Try.NonFatalException.of(new Exception()); - assertThat(cause).isNotNull(); + final Exception exception = new Exception(); + assertThat(Try.failure(exception).getCause()).isSameAs(exception); } @Test @@ -614,8 +621,8 @@ public void shouldSubsequentlyHandOverCause() { try { Try.of(outer::get).get(); Assertions.fail("Exception expected"); - } catch (Try.FatalException x) { - Assertions.assertThat(x.getCause().getMessage()).isEqualTo("\uD83D\uDCA9"); + } catch (UnknownError x) { + Assertions.assertThat(x.getMessage()).isEqualTo("\uD83D\uDCA9"); } catch (Throwable x) { Assertions.fail("Unexpected exception type: " + x.getClass().getName()); } @@ -630,22 +637,7 @@ public void shouldCreateFailureOnNonFatalException() { @Test public void shouldReturnAndNotThrowOnNonFatal() { - final Try.NonFatalException cause = Try.NonFatalException.of(new Exception()); - assertThat(Try.NonFatalException.of(cause)).isNotNull(); - } - - @Test - public void shouldReturnToStringOnNonFatal() { - final Exception exception = new java.lang.Exception(); - final Try.NonFatalException cause = Try.NonFatalException.of(exception); - assertThat(cause.toString()).isEqualTo("NonFatal(" + exception.toString() + ")"); - } - - @Test - public void shouldReturnHasCodeOnNonFatal() { - final Exception exception = new java.lang.Exception(); - final Try.NonFatalException cause = Try.NonFatalException.of(exception); - assertThat(cause.hashCode()).isEqualTo(Objects.hashCode(exception)); + assertThat(Try.failure(new Exception())).isNotNull(); } // -- Failure.Fatal @@ -654,24 +646,11 @@ public void shouldReturnHasCodeOnNonFatal() { public void shouldReturnToStringOnFatal() { try { Try.of(() -> { - throw new UnknownError(); - }); - fail("Exception Expected"); - } catch (Try.FatalException x) { - assertThat(x.toString()).isEqualTo("Fatal(java.lang.UnknownError)"); - } - } - - @Test - public void shouldReturnHashCodeOnFatal() { - UnknownError error = new UnknownError(); - try { - Try.of(() -> { - throw error; + throw new UnknownError("test"); }); fail("Exception Expected"); - } catch (Try.FatalException x) { - assertThat(x.hashCode()).isEqualTo(Objects.hashCode(error)); + } catch (UnknownError x) { + assertThat(x.getMessage()).isEqualTo("test"); } } @@ -683,13 +662,13 @@ public void shouldReturnEqualsOnFatal() { throw error; }); fail("Exception Expected"); - } catch (Try.FatalException x) { + } catch (UnknownError x) { try { Try.of(() -> { throw error; }); fail("Exception Expected"); - } catch (Try.FatalException fatal) { + } catch (UnknownError fatal) { assertThat(x.equals(fatal)).isEqualTo(true); } } @@ -704,7 +683,7 @@ public void shouldDetectFailureOfRunnable() { }).isFailure()).isTrue(); } - @Test(expected = Try.FatalException.class) + @Test(expected = UnknownError.class) public void shouldPassThroughFatalException() { Try.of(() -> { throw new UnknownError(); @@ -727,11 +706,22 @@ public void shouldDetectNonSuccessOnFailure() { // -- get - @Test(expected = Try.NonFatalException.class) + @Test(expected = RuntimeException.class) public void shouldThrowWhenGetOnFailure() { failure().get(); } + @Test + public void shouldThrowUndeclaredThrowableExceptionWhenUsingDynamicProxiesAndGetThrows() { + final Value testee = (Value) Proxy.newProxyInstance( + Value.class.getClassLoader(), + new Class[] { Value.class }, + (proxy, method, args) -> Try.failure(new Exception()).get()); + assertThatThrownBy(testee::get) + .isInstanceOf(UndeclaredThrowableException.class) + .hasCauseExactlyInstanceOf(Exception.class); + } + // -- getOrElse @Test @@ -849,9 +839,10 @@ public void shouldNotTryToRecoverWhenItIsNotNeeded(){ assertThat(Try.of(() -> OK).recoverWith(RuntimeException.class, x -> failure()).get()).isEqualTo(OK); } - @Test(expected = Try.NonFatalException.class) + @Test public void shouldReturnExceptionWhenRecoveryWasNotSuccess(){ - Try.of(() -> {throw error();}).recoverWith(IOException.class, x -> failure()).get(); + final Try testee = Try.of(() -> { throw error(); }).recoverWith(IOException.class, x -> failure()); + assertThatThrownBy(testee::get).isInstanceOf(RuntimeException.class).hasMessage("error"); } @Test @@ -868,8 +859,8 @@ public void shouldReturnRecoveredValue(){ @Test public void shouldHandleErrorDuringRecovering(){ - Try t = Try.of(() -> {throw new IllegalArgumentException(OK);}).recoverWith(IOException.class, x -> { throw new IllegalStateException(FAILURE);}); - assertThatThrownBy(t::get).hasRootCauseExactlyInstanceOf(IllegalArgumentException.class); + final Try t = Try.of(() -> {throw new IllegalArgumentException(OK);}).recoverWith(IOException.class, x -> { throw new IllegalStateException(FAILURE);}); + assertThatThrownBy(t::get).isInstanceOf(IllegalArgumentException.class); } // -- recoverWith(Class, Try) @@ -1101,7 +1092,7 @@ public void shouldMapFailureWhenFailureButDoesNotMatch() { ); assertThat(actual).isSameAs(testee); } - + // -- andThen @Test @@ -1173,7 +1164,7 @@ public void shouldEqualFailure() { @Test public void shouldHashFailure() { final Throwable error = error(); - assertThat(Try.failure(error).hashCode()).isEqualTo(Objects.hashCode(error)); + assertThat(Try.failure(error).hashCode()).isEqualTo(Arrays.hashCode(error.getStackTrace())); } // toString @@ -1304,9 +1295,10 @@ public void shouldFilterMatchingPredicateWithErrorProviderOnSuccess() { assertThat(success().filter(s -> true, s -> new IllegalArgumentException(s)).get()).isEqualTo(OK); } - @Test(expected = Try.NonFatalException.class) + @Test public void shouldFilterNonMatchingPredicateOnSuccess() { - success().filter(s -> false).get(); + final Try testee = success().filter(s -> false); + assertThatThrownBy(testee::get).isInstanceOf(NoSuchElementException.class); } @Test @@ -1326,7 +1318,7 @@ public void shouldUseErrorProviderWhenFilterNonMatchingPredicateOnSuccess() thro assertThat(success().filter(s -> false, str -> new IllegalArgumentException(str)).getCause()) .isInstanceOf(IllegalArgumentException.class); } - + @Test(expected = RuntimeException.class) public void shouldFilterWithExceptionOnSuccess() { success().filter(s -> { @@ -1370,16 +1362,18 @@ public void shouldMapOnSuccess() { assertThat(success().map(s -> s + "!").get()).isEqualTo(OK + "!"); } - @Test(expected = Try.NonFatalException.class) + @Test public void shouldMapWithExceptionOnSuccess() { - success().map(s -> { + final Try testee = success().map(s -> { throw new RuntimeException("xxx"); - }).get(); + }); + assertThatThrownBy(testee::get).isInstanceOf(RuntimeException.class).hasMessage("xxx"); } - @Test(expected = Try.NonFatalException.class) + @Test public void shouldThrowWhenCallingFailedOnSuccess() { - success().failed().get(); + final Try testee = success().failed(); + assertThatThrownBy(testee::get).isInstanceOf(NoSuchElementException.class); } @Test(expected = UnsupportedOperationException.class)