Use the {@link #toUnchecked()} method to get a standard Supplier that
9 | * wraps any checked exceptions thrown by {@link #get()} in a
10 | * {@link RuntimeException} (if they're not already RuntimeExceptions).
11 | *
12 | * @param the type of value produced by this Supplier
13 | */
14 | @FunctionalInterface
15 | public interface CheckedSupplier {
16 | T get() throws Exception;
17 |
18 | default Supplier toUnchecked() {
19 | return () -> {
20 | try {
21 | return get();
22 | } catch (Exception e) {
23 | if (e instanceof RuntimeException) {
24 | throw (RuntimeException) e;
25 | } else {
26 | throw new RuntimeException(e);
27 | }
28 | }
29 | };
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/io/meat/CheckedFunction.java:
--------------------------------------------------------------------------------
1 | package io.meat;
2 |
3 | import java.util.function.Function;
4 |
5 | /**
6 | * A {@link java.util.function.Function} which can throw any exception.
7 | *
8 | *
Use the {@link #toUnchecked()} method to get a standard Function that
9 | * wraps any checked exceptions thrown by the function in a
10 | * {@link RuntimeException} (if they're not already RuntimeExceptions).
11 | *
12 | * @param the input type of the function
13 | * @param the output type of the function
14 | */
15 | @FunctionalInterface
16 | public interface CheckedFunction {
17 | R apply(T t) throws Exception;
18 |
19 | default Function toUnchecked() {
20 | return (input) -> {
21 | try {
22 | return apply(input);
23 | } catch (Exception e) {
24 | if (e instanceof RuntimeException) {
25 | throw (RuntimeException) e;
26 | } else {
27 | throw new RuntimeException(e);
28 | }
29 | }
30 | };
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/java/io/meat/TryFutureTest.java:
--------------------------------------------------------------------------------
1 | package io.meat;
2 |
3 | import java.util.concurrent.CompletableFuture;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | public class TryFutureTest {
10 | @Test
11 | public void tryWrapCompletableFutureSucceeds() throws Exception {
12 | CompletableFuture future = new CompletableFuture<>();
13 | CompletableFuture> wrappedFuture = Try.wrapFuture(future);
14 | future.complete("It worked!");
15 | assertEquals("A successful wrapped future should contain the future's result as a successful Try",
16 | Try.succeed("It worked!"),
17 | wrappedFuture.get());
18 | }
19 |
20 | @Test
21 | public void tryWrapCompletableFutureFails() throws Exception {
22 | IllegalStateException error = new IllegalStateException("Something broke!");
23 | CompletableFuture future = new CompletableFuture<>();
24 | CompletableFuture> wrappedFuture = Try.wrapFuture(future);
25 | future.completeExceptionally(error);
26 | assertEquals("A failed wrapped future should contain a failed Try",
27 | Try.fail(error),
28 | wrappedFuture.get());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/src/test/java/io/meat/CheckedSupplierTest.java:
--------------------------------------------------------------------------------
1 | package io.meat;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 | import java.util.function.Supplier;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.fail;
10 |
11 | public class CheckedSupplierTest {
12 |
13 | @Test
14 | public void testToUncheckedWrapsCheckedExceptions() throws Exception {
15 | CheckedSupplier func = () -> {
16 | throw new IOException("Hello");
17 | };
18 | Supplier unchecked = func.toUnchecked();
19 | try {
20 | unchecked.get();
21 | fail("Calling the Supplier should have raised an exception");
22 | } catch (RuntimeException e) {
23 | assertEquals(
24 | "The cause of the RuntimeException should be the unchecked exception",
25 | e.getCause().getClass(),
26 | IOException.class);
27 | }
28 | }
29 |
30 | @Test
31 | public void testToUncheckedDoesntWrapUncheckedExceptions() throws Exception {
32 | CheckedSupplier func = () -> {
33 | throw new ArithmeticException("Hello");
34 | };
35 | Supplier unchecked = func.toUnchecked();
36 | try {
37 | unchecked.get();
38 | fail("Calling the Supplier should have raised an exception");
39 | } catch (RuntimeException e) {
40 | assertEquals(
41 | "The exception should not be wrapped in another RuntimeException",
42 | e.getClass(),
43 | ArithmeticException.class);
44 | }
45 | }
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/src/test/java/io/meat/CheckedFunctionTest.java:
--------------------------------------------------------------------------------
1 | package io.meat;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 | import java.util.function.Function;
7 |
8 | import static org.junit.Assert.assertEquals;
9 | import static org.junit.Assert.fail;
10 |
11 | public class CheckedFunctionTest {
12 |
13 | @Test
14 | public void testToUncheckedWrapsCheckedExceptions() throws Exception {
15 | CheckedFunction func = integer -> {
16 | throw new IOException("Hello");
17 | };
18 | Function unchecked = func.toUnchecked();
19 | try {
20 | unchecked.apply(123);
21 | fail("Applying the function should have raised an exception");
22 | } catch (RuntimeException e) {
23 | assertEquals(
24 | "The cause of the RuntimeException should be the unchecked exception",
25 | e.getCause().getClass(),
26 | IOException.class);
27 | }
28 | }
29 |
30 | @Test
31 | public void testToUncheckedDoesntWrapUncheckedExceptions() throws Exception {
32 | CheckedFunction func = integer -> {
33 | throw new ArithmeticException("Hello");
34 | };
35 | Function unchecked = func.toUnchecked();
36 | try {
37 | unchecked.apply(123);
38 | fail("Applying the function should have raised an exception");
39 | } catch (RuntimeException e) {
40 | assertEquals(
41 | "The exception should not be wrapped in another RuntimeException",
42 | e.getClass(),
43 | ArithmeticException.class);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # io.meat:try
2 | ### A humble Try monad for Java 8
3 |
4 | A Try is a completed attempt to compute a result that either succeeded or
5 | failed.
6 |
7 | ## Quickstart
8 |
9 | Try is especially useful for monadic composition of tasks that may each fail,
10 | without having to use a series of nested try/catch statements. You can form a
11 | Try using `attempt()` or `attemptApply()`:
12 |
13 | ```java
14 | Try value = Try.attempt(() -> keyValueStore.fetch("someKey"));
15 | Try value = Try.attemptApply(keyValueStore::fetch, "someKey");
16 | ```
17 |
18 | If the function has checked exceptions, you can use `attemptChecked()` or
19 | `attemptApplyChecked()` to wrap any exception in a RuntimeException.
20 |
21 | You can also manually make a successful or failed Try using `succeed()` and
22 | `fail()`:
23 |
24 | ```java
25 | Try value = Try.succeed("someValue");
26 | Try failure = Try.fail(new IllegalStateException("Something broke"));
27 | ```
28 |
29 | Once you have a Try, you can transform the value inside it using `map()`:
30 |
31 | ```java
32 | Try hex = Try.succeed(123).map(Integer::toHexString);
33 | assert hex.equals(Try.succeed("7b"));
34 | ```
35 |
36 | If your function returns a Try, you can use `flatMap()` to prevent nesting:
37 |
38 | ```java
39 | Try number = Try.succeed(123);
40 | Try nextValue = number.flatMap(num -> {
41 | return Try.succeed("a string");
42 | });
43 | assert nextValue.equals(Try.succeed("a string"));
44 | ```
45 |
46 | To get values back out of a Try, there are Optionals available as `getResult()`
47 | and `getFailure()` for safe retrieval of either state:
48 |
49 | ```java
50 | Try number = Try.succeed(123);
51 | assert number.getResult().equals(Optional.of(123));
52 | assert !number.getFailure().isPresent();
53 | ```
54 |
55 | If you've checked that a Try is successful, or you don't mind an (unchecked)
56 | exception being raised, use `get()`:
57 |
58 | ```java
59 | Try number = Try.succeed(123);
60 | assert number.get() == 123;
61 | ```
62 |
63 | In the case of a failed Try, `get()` will wrap the exception in a
64 | `RuntimeException`; its `Throwable.getCause()` will be the original exception.
65 |
66 | ## API Docs
67 |
68 | The API docs are hosted [on Github Pages](http://zacharyvoase.github.io/try/apidocs/).
69 |
70 |
71 | ## Unlicense
72 |
73 | This is free and unencumbered software released into the public domain.
74 |
75 | Anyone is free to copy, modify, publish, use, compile, sell, or
76 | distribute this software, either in source code form or as a compiled
77 | binary, for any purpose, commercial or non-commercial, and by any
78 | means.
79 |
80 | In jurisdictions that recognize copyright laws, the author or authors
81 | of this software dedicate any and all copyright interest in the
82 | software to the public domain. We make this dedication for the benefit
83 | of the public at large and to the detriment of our heirs and
84 | successors. We intend this dedication to be an overt act of
85 | relinquishment in perpetuity of all present and future rights to this
86 | software under copyright law.
87 |
88 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
91 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
92 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
93 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
94 | OTHER DEALINGS IN THE SOFTWARE.
95 |
96 | For more information, please refer to
97 |
98 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | io.meat
6 | try
7 | 0.0.2
8 | jar
9 |
10 | try
11 | A Try monad for Java 8
12 | https://github.com/zacharyvoase/try
13 |
14 |
15 |
16 | Zachary Voase
17 | zack@meat.io
18 | https://twitter.com/meat
19 |
20 |
21 |
22 |
23 | scm:git:git@github.com:zacharyvoase/try.git
24 | scm:git:git@github.com:zacharyvoase/try.git
25 | git@github.com:zacharyvoase/try.git
26 |
27 |
28 |
29 |
30 | Unlicense
31 | http://unlicense.org/UNLICENSE
32 | repo
33 | This is free and unencumbered software released into the public domain.
34 |
35 |
36 |
37 |
38 |
39 | ossrh
40 | https://oss.sonatype.org/content/repositories/snapshots
41 |
42 |
43 |
44 |
45 | UTF-8
46 |
47 |
48 |
49 |
50 | junit
51 | junit
52 | 4.12
53 | test
54 |
55 |
56 |
57 |
58 |
59 |
60 | org.apache.maven.plugins
61 | maven-compiler-plugin
62 | 3.3
63 |
64 | 1.8
65 | 1.8
66 |
67 |
68 |
69 |
70 | org.apache.maven.plugins
71 | maven-site-plugin
72 | 3.3
73 |
74 | true
75 |
76 |
77 |
78 |
79 | org.apache.maven.plugins
80 | maven-source-plugin
81 |
82 |
83 | attach-sources
84 |
85 | jar
86 |
87 |
88 |
89 |
90 |
91 |
92 | org.apache.maven.plugins
93 | maven-javadoc-plugin
94 | 2.10.3
95 |
96 |
97 | attach-javadocs
98 |
99 | jar
100 |
101 |
102 |
103 |
104 |
105 |
106 | com.github.github
107 | site-maven-plugin
108 | 0.12
109 |
110 | github
111 | Updated JavaDocs for ${project.version}
112 |
113 |
114 |
115 |
116 | site
117 |
118 | site-deploy
119 |
120 |
121 |
122 |
123 |
124 | org.apache.maven.plugins
125 | maven-gpg-plugin
126 | 1.6
127 |
128 |
129 | sign-artifacts
130 | verify
131 |
132 | sign
133 |
134 |
135 |
136 |
137 |
138 |
139 | org.sonatype.plugins
140 | nexus-staging-maven-plugin
141 | 1.6.3
142 | true
143 |
144 | ossrh
145 | https://oss.sonatype.org/
146 | true
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | org.apache.maven.plugins
156 | maven-project-info-reports-plugin
157 | 2.8
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 | org.apache.maven.plugins
168 | maven-javadoc-plugin
169 | 2.10.3
170 |
171 | ${project.reporting.outputDirectory}
172 |
173 |
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/src/main/java/io/meat/Try.java:
--------------------------------------------------------------------------------
1 | package io.meat;
2 |
3 | import java.util.Objects;
4 | import java.util.Optional;
5 | import java.util.concurrent.CompletableFuture;
6 | import java.util.function.Function;
7 | import java.util.function.Supplier;
8 | import java.util.stream.Stream;
9 |
10 | /**
11 | * A completed attempt to compute a result that either succeeded or failed.
12 | *
13 | *
Try is especially useful for monadic composition of tasks that may either
14 | * succeed or fail, without having to use a series of nested try/catch
15 | * statements. You can form a Try using {@link #attempt(Supplier)} or
16 | * {@link #attemptApply(Function, Object)}:
If the function has checked exceptions, you can use
24 | * {@link #attemptChecked(CheckedSupplier)} or
25 | * {@link #attemptApplyChecked(CheckedFunction, Object)} to wrap any exception
26 | * in a RuntimeException.
27 | *
28 | *
You can also manually make a successful or failed Try using
29 | * {@link #succeed(Object)} and {@link #fail(Throwable)}:
To get values back out of a Try, there are Optionals available as
56 | * {@link #getResult()} and {@link #getFailure()} for safe retrieval of either
57 | * state:
In the case of a failed Try, get() will wrap the exception in a
74 | * RuntimeException; its {@link Throwable#getCause()} will be the
75 | * original exception.
76 | *
77 | * @param the type of result, if successful.
78 | */
79 | public final class Try {
80 |
81 | private final Result result;
82 | private final Throwable failure;
83 |
84 | private Try(Result result, Throwable failure) {
85 | assert (result == null) ^ (failure == null)
86 | : "Exactly one of failure or result must be null";
87 | this.result = result;
88 | this.failure = failure;
89 | }
90 |
91 | /**
92 | * Build a successful Try from a non-null result.
93 | */
94 | public static Try succeed(Result result) {
95 | if (result == null) {
96 | throw new IllegalArgumentException("Try.succeed result may not be null");
97 | }
98 | return new Try<>(result, null);
99 | }
100 |
101 | /**
102 | * Build a failed Try from an exception or other Throwable.
103 | */
104 | public static Try fail(Throwable failure) {
105 | if (failure == null) {
106 | throw new IllegalArgumentException("Try.fail failure may not be null");
107 | }
108 | return new Try<>(null, failure);
109 | }
110 |
111 | /**
112 | * Wrap the result of a {@link Supplier} as a Try, catching exceptions.
113 | *
114 | * @param func a Supplier which returns a Result
115 | * @param the type of result returned by func
116 | * @return a Try containing either the Supplier's result, or any exception
117 | * thrown by {@link Supplier#get()}
118 | */
119 | public static Try attempt(Supplier func) {
120 | return attemptChecked(func::get);
121 | }
122 |
123 | /**
124 | * Similar to {@link #attempt(Supplier)}, but handles checked exceptions.
125 | * @param func a CheckedSupplier which returns a Result or throws Exception
126 | * @param the type of result returned by func
127 | * @return a Try containing either the CheckedSupplier's result, or any
128 | * exception thrown by {@link CheckedSupplier#get()}
129 | */
130 | public static Try attemptChecked(CheckedSupplier func) {
131 | Result result;
132 | try {
133 | result = func.get();
134 | } catch (Exception e) {
135 | return Try.fail(e);
136 | }
137 | return Try.succeed(result);
138 | }
139 |
140 |
141 | /**
142 | * Wrap the result of a {@link Function} as a Try, catching exceptions.
143 | *
144 | * @param func a Function from the input to the result type of the Try
145 | * @param input a single argument for func
146 | * @param the input type of the function
147 | * @param the result type of the function
148 | * @return a Try of type Result
149 | */
150 | public static Try attemptApply(
151 | Function super Input, ? extends Result> func,
152 | Input input) {
153 | return attemptApplyChecked(func::apply, input);
154 | }
155 |
156 | /**
157 | * Similar to {@link #attemptApply(Function, Object)}, handling checked exceptions.
158 | *
159 | * @param func a Function from the input to the result type of the Try
160 | * @param input a single argument for func
161 | * @param the input type of the function
162 | * @param the result type of the function
163 | * @return a Try of type Result
164 | */
165 | public static Try attemptApplyChecked(
166 | CheckedFunction super Input, ? extends Result> func,
167 | Input input) {
168 | Result result;
169 | try {
170 | result = func.apply(input);
171 | } catch (Exception e) {
172 | return Try.fail(e);
173 | }
174 | return Try.succeed(result);
175 | }
176 |
177 | public static CompletableFuture> wrapFuture(CompletableFuture future) {
178 | return future.handle(Try::new);
179 | }
180 |
181 | /**
182 | * Get the successful result of this Try, or {@link Optional#empty()} if it failed.
183 | *
184 | * @return an Optional result
185 | */
186 | public Optional getResult() {
187 | return Optional.ofNullable(result);
188 | }
189 |
190 | /**
191 | * Get the Throwable that caused this Try to fail, or {@link Optional#empty()} if it was successful.
192 | *
193 | * @return an Optional Throwable
194 | */
195 | public Optional getFailure() {
196 | return Optional.ofNullable(failure);
197 | }
198 |
199 | /**
200 | * Get this Try's result, or throw its failure as an unchecked exception.
201 | *
202 | * @return the successful result of this Try
203 | * @throws RuntimeException wrapping the failure as an unchecked exception
204 | */
205 | public Result get() {
206 | if (result == null) {
207 | throw new RuntimeException(failure);
208 | }
209 | return result;
210 | }
211 |
212 | /**
213 | * Turn this Try into a Stream, for flatMapping a {@code Stream>} into a {@code Stream}.
214 | *
215 | *
This method is particularly useful as an argument to Stream#flatMap():
222 | *
223 | * @return A single-element Stream.of(result) if this Try is successful, otherwise Stream.empty()
224 | */
225 | public Stream stream() {
226 | if (result == null) {
227 | return Stream.empty();
228 | }
229 | return Stream.of(result);
230 | }
231 |
232 | /**
233 | * Transform the result of this Try.
234 | *
235 | *
If this Try is successful, the provided function will be applied to
236 | * the current result and a new Try of the destination type will be
237 | * returned.
238 | *
If this Try is a failure, a new Try of the destination result type
239 | * containing the existing failure will be returned.
240 | *
If this Try is successful but the mapping function throws an
241 | * exception, a failed Try of the destination result type will be
242 | * returned, containing that exception.
243 | *
244 | * @param func A function mapping this Try's result type to a new one
245 | * @param the output type of the transformation function
246 | * @return a new Try of either the transformed result or existing failure
247 | */
248 | public Try map(Function super Result, ? extends NewResult> func) {
249 | if (result == null) { return Try.fail(this.failure); }
250 | return Try.attemptApply(func, result);
251 | }
252 |
253 | /**
254 | * Transform the result of this Try into a new Try, returning that.
255 | *
256 | *
The behavior of this function is similar to {@link #map(Function)},
257 | * except that func should return a Try, and this will be returned without
258 | * being wrapped further.
259 | *
Any uncaught exceptions thrown by func will themselves be captured in
260 | * a Try.
261 | *
262 | * @param func a function mapping this Try's result type to another Try
263 | * @param the result type of the Try produced by func
264 | * @return a Try of the destination result type
265 | */
266 | public Try flatMap(Function super Result, Try> func) {
267 | if (result == null) { return Try.fail(this.failure); }
268 | // This is kind of like Try.attemptApply but we don't wrap the result
269 | // of func.apply in Try.succeed.
270 | try {
271 | return func.apply(result);
272 | } catch (Exception e) {
273 | return Try.fail(e);
274 | }
275 | }
276 |
277 | @Override
278 | public boolean equals(Object o) {
279 | if (this == o) return true;
280 | if (o == null || getClass() != o.getClass()) return false;
281 | Try> aTry = (Try>) o;
282 | return Objects.equals(result, aTry.result) &&
283 | Objects.equals(failure, aTry.failure);
284 | }
285 |
286 | @Override
287 | public int hashCode() {
288 | return Objects.hash(result, failure);
289 | }
290 |
291 | @Override
292 | public String toString() {
293 | if (result != null) {
294 | return "Try{result=" + result + "}";
295 | } else {
296 | return "Try{failure=" + failure + "}";
297 | }
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/src/test/java/io/meat/TryTest.java:
--------------------------------------------------------------------------------
1 | package io.meat;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.IOException;
6 | import java.util.function.Function;
7 | import java.util.stream.Stream;
8 |
9 | import static org.junit.Assert.*;
10 |
11 | public class TryTest {
12 | @Test(expected = IllegalArgumentException.class)
13 | public void testCannotSucceedWithNull() {
14 | Try.succeed(null);
15 | }
16 |
17 | @Test(expected = IllegalArgumentException.class)
18 | public void testCannotFailWithNull() {
19 | Try.fail(null);
20 | }
21 |
22 | @Test
23 | public void testResultAndFailureForSuccess() {
24 | Try successful = Try.succeed(123);
25 | assertTrue(
26 | "getResult() of a successful Try should be a non-empty Optional",
27 | successful.getResult().isPresent());
28 | assertEquals(successful.getResult().get(), (Integer) 123);
29 | assertFalse(
30 | "getFailure() of a successful Try should be an empty Optional",
31 | successful.getFailure().isPresent());
32 | }
33 |
34 | @Test
35 | public void testResultAndFailureForFailure() {
36 | Exception error = new IllegalStateException("Something went wrong");
37 | Try failed = Try.fail(error);
38 | assertFalse(
39 | "getResult() of a failed Try should be an empty Optional",
40 | failed.getResult().isPresent());
41 | assertTrue(
42 | "getFailure() of a failed Try should be a non-empty Optional",
43 | failed.getFailure().isPresent());
44 | assertEquals(failed.getFailure().get(), error);
45 | }
46 |
47 | @Test
48 | public void testAttemptReturnsSuccess() {
49 | Try successful = Try.attempt(() -> 123);
50 | assertEquals(
51 | "Try.attempt() should return a successful Try for a successful attempt",
52 | Try.succeed(123), successful);
53 | }
54 |
55 | @Test
56 | public void testAttemptCapturesRuntimeExceptions() {
57 | IllegalStateException error = new IllegalStateException("Things broke");
58 | Try failed = Try.attempt(() -> {
59 | if (true) {
60 | throw error;
61 | } else {
62 | // this will never happen
63 | return 123;
64 | }
65 | });
66 | assertEquals(
67 | "Try.attempt() should return a failed Try for an uncaught exception",
68 | Try.fail(error), failed);
69 | }
70 |
71 | @Test
72 | public void testAttemptCheckedReturnsSuccess() {
73 | Try successful = Try.attemptChecked(() -> 123);
74 | assertEquals(
75 | "Try.attemptChecked() should return a successful Try for a successful attempt",
76 | Try.succeed(123), successful);
77 | }
78 |
79 | @Test
80 | public void testAttemptCheckedCapturesCheckedExceptions() {
81 | IOException error = new IOException("Things broke");
82 | Try failed = Try.attemptChecked(() -> {
83 | if (true) {
84 | throw error;
85 | } else {
86 | // this will never happen
87 | return 123;
88 | }
89 | });
90 | assertEquals(
91 | "Try.attemptChecked() should return a failed Try for a checked exception",
92 | Try.fail(error), failed);
93 | }
94 |
95 | @Test
96 | public void testAttemptApplyReturnsSuccess() {
97 | Try successful = Try.attemptApply(Integer::toHexString, 123);
98 | assertEquals(
99 | "Try.attemptApply() should return a successful Try for a successful application",
100 | Try.succeed("7b"), successful);
101 | }
102 |
103 | @Test
104 | public void testAttemptApplyCapturesRuntimeExceptions() {
105 | Try failed = Try.attemptApply(number -> number / 0, 123);
106 | assertEquals(
107 | "Try.attemptApply() should return a failed Try for an uncaught exception",
108 | ArithmeticException.class,
109 | failed.getFailure().get().getClass());
110 | }
111 |
112 | @Test
113 | public void testAttemptApplyCheckedReturnsSuccess() {
114 | Try successful = Try.attemptApplyChecked(Integer::toHexString, 123);
115 | assertEquals(
116 | "Try.attemptApplyChecked() should return a successful Try for a successful application",
117 | Try.succeed("7b"), successful);
118 | }
119 |
120 | @Test
121 | public void testAttemptApplyCheckedCapturesCheckedExceptions() {
122 | IOException error = new IOException("Things broke");
123 | Try failed = Try.attemptApplyChecked(number -> {
124 | if (true) {
125 | throw error;
126 | } else {
127 | return "this will never happen";
128 | }
129 | }, 123);
130 | assertEquals(
131 | "Try.attemptApplyChecked() should return a failed Try for a checked exception",
132 | Try.fail(error), failed);
133 | }
134 |
135 | @Test
136 | public void testGetShouldReturnResultForSuccess() {
137 | String value = "A successful value";
138 | assertEquals(
139 | "Try.get() should return the result for a successful Try",
140 | value,
141 | Try.succeed(value).get());
142 | }
143 |
144 | @Test
145 | public void testGetShouldThrowAnUncheckedExceptionForFailure() {
146 | ArithmeticException error = new ArithmeticException("Probably divided by zero");
147 | try {
148 | Try.fail(error).get();
149 | fail("Try.get() should throw an unchecked exception for a failed Try");
150 | } catch (Exception e) {
151 | assertEquals(
152 | "Try.get() should throw a RuntimeException",
153 | RuntimeException.class,
154 | e.getClass());
155 | assertEquals(
156 | "Try.get() should throw a RuntimeException caused by the failure of the Try",
157 | error,
158 | e.getCause());
159 | }
160 | }
161 |
162 | @Test
163 | public void testStreamShouldRemoveFailedTrys() {
164 | Stream> numberTries = Stream.of("123", "456", "abc", "789")
165 | .map(s -> Try.attemptApply(Integer::parseInt, s));
166 | Stream numbers = Stream.of("123", "456", "abc", "789")
167 | .map(s -> Try.attemptApply(Integer::parseInt, s))
168 | .flatMap(Try::stream);
169 | assertEquals("The original mapped stream should contain both failed and successful Trys",
170 | 4,
171 | numberTries.toArray().length);
172 | assertArrayEquals("A flatMapped stream of Trys should not contain entries for failures",
173 | Stream.of(123, 456, 789).toArray(),
174 | numbers.toArray());
175 | }
176 |
177 | @Test
178 | public void testTrySuccessString() {
179 | assertEquals("Try{result=123}", Try.succeed(123).toString());
180 | }
181 |
182 | @Test
183 | public void testTryFailureString() {
184 | Exception error = new IllegalStateException("Some error");
185 | assertEquals(
186 | "Try{failure=java.lang.IllegalStateException: Some error}",
187 | Try.fail(error).toString());
188 | }
189 |
190 | @Test
191 | public void testTryHashCode() {
192 | Try result1 = Try.succeed(123);
193 | Try result2 = Try.succeed("Hello");
194 | Try result3 = Try.succeed("Hello");
195 | Try result4 = Try.succeed("World");
196 | Exception error1 = new ArithmeticException("Things got divided by zero");
197 | Exception error2 = new IllegalStateException("Things broke");
198 | Try failure1 = Try.fail(error1);
199 | Try failure2 = Try.fail(error2);
200 | Try failure3 = Try.fail(error1);
201 | Try failure4 = Try.fail(error1);
202 |
203 | assertEquals("Identical successful Trys should have the same hashCode",
204 | result2.hashCode(), result2.hashCode());
205 | assertEquals("Same type and equal but distinct successful Trys should have the same hashCode",
206 | result2.hashCode(), result3.hashCode());
207 | assertNotEquals("Same type but unequal successful Trys should not have the same hashCode",
208 | result3.hashCode(), result4.hashCode());
209 | assertNotEquals("Differently-typed successful Trys should not have the same hashCode",
210 | result1.hashCode(), result2.hashCode());
211 |
212 | assertEquals("Same type failed Trys with equal exceptions should have the same hashCode",
213 | failure1.hashCode(), failure4.hashCode());
214 | assertEquals("Differently-typed failed Trys with equal exceptions should have the same hashCode",
215 | failure1.hashCode(), failure3.hashCode());
216 | assertNotEquals("Same-typed failed Trys with different exceptions should not have the same hashCode",
217 | failure1.hashCode(), failure2.hashCode());
218 | assertNotEquals("Differently-typed failed Trys with different exceptions should not have the same hashCode",
219 | failure2.hashCode(), failure3.hashCode());
220 | }
221 |
222 | @Test
223 | public void testTryEquality() {
224 | Try result1 = Try.succeed(123);
225 | Try result2 = Try.succeed("Hello");
226 | Try result3 = Try.succeed("Hello");
227 | Try result4 = Try.succeed("World");
228 | Exception error1 = new ArithmeticException("Things got divided by zero");
229 | Exception error2 = new IllegalStateException("Things broke");
230 | Try failure1 = Try.fail(error1);
231 | Try failure2 = Try.fail(error2);
232 | Try failure3 = Try.fail(error1);
233 | Try failure4 = Try.fail(error1);
234 |
235 | assertEquals("Identical successful Trys should be equal",
236 | result2, result2);
237 | assertEquals("Same type and equal but distinct successful Trys should be equal",
238 | result2, result3);
239 | assertNotEquals("Same type but unequal successful Trys should not be equal",
240 | result3, result4);
241 | assertNotEquals("Differently-typed successful Trys should not be equal",
242 | result1, result2);
243 |
244 | assertEquals("Same type failed Trys with equal exceptions should be equal",
245 | failure1, failure4);
246 | assertEquals("Differently-typed failed Trys with equal exceptions should be equal",
247 | failure1, failure3);
248 | assertNotEquals("Same-typed failed Trys with different exceptions should not be equal",
249 | failure1, failure2);
250 | assertNotEquals("Differently-typed failed Trys with different exceptions should not be equal",
251 | failure2, failure3);
252 | }
253 |
254 | @Test
255 | public void testMapOnSuccessReturnsNewResult() {
256 | Try successful = Try.succeed(123);
257 | Try mapped = successful.map(Integer::toHexString);
258 | assertEquals(
259 | "map() on a successful Try should return the transformed result",
260 | Try.succeed("7b"), mapped);
261 | }
262 |
263 | @Test
264 | public void testMapOnSuccessWhichThrowsReturnsNewFailure() {
265 | ArithmeticException error = new ArithmeticException("Pretend we had a divide by zero");
266 | Try successful = Try.succeed(123);
267 | Try mapped = successful.map((number) -> {
268 | if (true) {
269 | throw error;
270 | } else {
271 | return "this will never happen";
272 | }
273 | });
274 | assertEquals(
275 | "If the mapping function fails, the result should be a failure of the exception it threw",
276 | Try.fail(error), mapped);
277 | }
278 |
279 | @Test
280 | public void testMapOnFailureReturnsOldFailure() {
281 | Exception error = new IllegalStateException("Something went wrong");
282 | Try failed = Try.fail(error);
283 | Try mapped = failed.map((number) -> {
284 | throw new ArithmeticException(
285 | "The map function should never be called for a failed Try");
286 | });
287 | assertEquals(
288 | "Mapping a failed Try should produce the same failed Try",
289 | failed, mapped);
290 | }
291 |
292 | @Test
293 | public void testFlatMapOnSuccessReturningSuccessReturnsNewResult() {
294 | Try successful = Try.succeed(123);
295 | Try flatMapped = successful
296 | .flatMap((number) -> Try.succeed(Integer.toHexString(number)));
297 | assertEquals(
298 | "flatMapping a successful Try should produce a new Try of the result",
299 | Try.succeed("7b"), flatMapped);
300 | }
301 |
302 | @Test
303 | public void testFlatMapOnSuccessReturningFailureReturnsNewFailure() {
304 | Exception error = new ArithmeticException("Someone tried to divide by zero");
305 | Try successful = Try.succeed(123);
306 | Try flatMapped = successful
307 | .flatMap((number) -> Try.fail(error));
308 | assertEquals(
309 | "flatMapping to a failure should produce that failure",
310 | Try.fail(error), flatMapped);
311 | }
312 |
313 | @Test
314 | public void testFlatMapOnSuccessWhichThrowsReturnsNewFailure() {
315 | IllegalStateException error = new IllegalStateException("Everything broke");
316 | Try successful = Try.succeed(123);
317 | Try flatMapped = successful
318 | .flatMap((number) -> {
319 | if (true) {
320 | throw error;
321 | } else {
322 | return Try.succeed("this will never happen");
323 | }
324 | });
325 | assertEquals(
326 | "If the flatMapping function fails, the result should be a failure of the exception it threw",
327 | Try.fail(error), flatMapped);
328 | }
329 | }
330 |
--------------------------------------------------------------------------------