View Javadoc

1   package io.atlassian.fugue;
2   
3   import java.util.ArrayList;
4   import java.util.NoSuchElementException;
5   import java.util.function.Function;
6   import java.util.function.Supplier;
7   
8   import static java.util.Collections.unmodifiableList;
9   import static java.util.Objects.requireNonNull;
10  import static java.util.function.Function.identity;
11  
12  /**
13   * A <code>Try</code> represents a computation that may either throw an
14   * exception or return a value. A Try will either be {@link Try.Success Success}
15   * wrapping a value or {@link Try.Failure Failure} which wraps an exception.
16   * <p>
17   * This class is similar to {@link Either}, but is explicit about having a
18   * success and failure case. Unless method level javadoc says otherwise, methods
19   * will not automatically catch exceptions thrown by function arguments. In
20   * particular map will not catch automatically catch thrown exceptions, instead
21   * you should use {@link Checked#lift} to to make the function explicitly return
22   * a Try and the use flatmap.
23   *
24   * @since 4.4.0
25   */
26  public abstract class Try<A> {
27    /**
28     * Creates a new failure
29     *
30     * @param e an exception to wrap, must not be null.
31     * @param <A> the success type
32     * @return a new Failure wrapping e.
33     */
34    public static <A> Try<A> failure(final Exception e) {
35      return new Failure<>(e);
36    }
37  
38    /**
39     * Creates a new Success
40     *
41     * @param value a value to wrap, must not be null
42     * @param <A> the wrapped value type
43     * @return a new Success wrapping v
44     */
45    public static <A> Try<A> successful(final A value) {
46      return new Success<>(value);
47    }
48  
49    /**
50     * Returns a success wrapping all of the values if all of the arguments were a
51     * success, otherwise this returns the first failure
52     *
53     * @param trys an iterable of try values
54     * @param <A> The success type
55     * @return a success wrapping all of the values if all of the arguments were a
56     * success, otherwise this returns the first failure
57     */
58    public static <A> Try<Iterable<A>> sequence(Iterable<Try<A>> trys) {
59      final ArrayList<A> ts = new ArrayList<>();
60      for (final Try<A> t : trys) {
61        if (t.isFailure()) {
62          return new Failure<>(t.fold(identity(), x -> {
63            throw new NoSuchElementException();
64          }));
65        }
66        ts.add(t.fold(f -> {
67          throw new NoSuchElementException();
68        }, identity()));
69      }
70      return new Success<>(unmodifiableList(ts));
71    }
72  
73    /**
74     * Reduces a nested Try by a single level
75     *
76     * @param t A nested Try
77     * @param <A> The success type
78     * @return The flattened try
79     */
80    public static <A> Try<A> flatten(Try<Try<A>> t) {
81      return t.flatMap(identity());
82    }
83  
84    /**
85     * Returns <code>true</code> if this failure, otherwise <code>false</code>
86     *
87     * @return <code>true</code> if this failure, otherwise <code>false</code>
88     */
89    public abstract boolean isFailure();
90  
91    /**
92     * Returns <code>true</code> if this success, otherwise <code>false</code>
93     *
94     * @return <code>true</code> if this success, otherwise <code>false</code>
95     */
96    public abstract boolean isSuccess();
97  
98    /**
99     * Binds the given function across the success value if it is one.
100    *
101    * @param <B> result type
102    * @param f the function to bind.
103    * @return A new Try value after binding with the function applied if this is
104    * a Success, otherwise returns this if this is a `Failure`.
105    */
106   public abstract <B> Try<B> flatMap(Function<? super A, Try<B>> f);
107 
108   /**
109    * Maps the given function to the value from this `Success` or returns this
110    * unchanged if a `Failure`.
111    *
112    * @param <B> result type
113    * @param f the function to apply
114    * @return `f` applied to the `Success`, otherwise returns this if this is a
115    * `Failure`.
116    */
117   public abstract <B> Try<B> map(Function<? super A, ? extends B> f);
118 
119   /**
120    * Applies the given function `f` if this is a `Failure` otherwise this
121    * unchanged if a 'Success'. This is like map for the failure.
122    *
123    * @param f the function to apply
124    * @return `f` applied to the `Failure`, otherwise returns this if this is a
125    * `Success`.
126    */
127   public abstract Try<A> recover(Function<? super Exception, A> f);
128 
129   /**
130    * Applies the given function `f` if this is a `Failure` with certain
131    * exception type otherwise leaves this unchanged. This is like map for
132    * exceptions types.
133    *
134    * @param exceptionType exception class
135    * @param f the function to apply
136    * @param <X> exception type
137    * @return `f` applied to the `Failure`, otherwise returns this if this is a
138    * `Success` or the exception does not match the exception type.
139    */
140   public abstract <X extends Exception> Try<A> recover(Class<X> exceptionType, Function<? super X, A> f);
141 
142   /**
143    * Binds the given function across the failure value if it is one, otherwise
144    * this unchanged if a 'Success'. This is like flatmap for the failure.
145    *
146    * @param f the function to bind.
147    * @return A new Try value after binding with the function applied if this is
148    * a `Failure`, otherwise returns this if this is a `Success`.
149    */
150   public abstract Try<A> recoverWith(Function<? super Exception, Try<A>> f);
151 
152   /**
153    * Binds the given function across certain exception type if it is one,
154    * otherwise this unchanged. This is like flatmap for exceptions types.
155    *
156    * @param exceptionType exception class
157    * @param f the function to apply
158    * @param <X> exception type
159    * @return A new Try value after binding with the function applied if this is
160    * a `Failure`, otherwise returns this if this is a `Success` or the exception
161    * does not match the exception type.
162    */
163   public abstract <X extends Exception> Try<A> recoverWith(Class<X> exceptionType, Function<? super X, Try<A>> f);
164 
165   /**
166    * Returns the contained value if this is a success otherwise call the
167    * supplier and return its value.
168    *
169    * @param s called if this is a failure
170    * @return the wrapped value or the value from the {@code Supplier}
171    */
172   public abstract A getOrElse(Supplier<A> s);
173 
174   /**
175    * Applies the function to the wrapped value, applying failureF it this is a
176    * Left and successF if this is a Right.
177    *
178    * @param failureF the function to apply if this is a Failure
179    * @param successF the function to apply if this is a Success
180    * @param <B> the destination type
181    * @return the result of the applied function
182    */
183   public abstract <B> B fold(Function<? super Exception, B> failureF, Function<A, B> successF);
184 
185   /**
186    * Convert this Try to an {@link Either}, becoming a left if this is a failure
187    * and a right if this is a success.
188    *
189    * @return this value wrapped in right if a success, and the exception wrapped
190    * in a left if a failure.
191    */
192   public abstract Either<Exception, A> toEither();
193 
194   /**
195    * Convert this Try to an Option. Returns <code>Some</code> with a value if it
196    * is a success, otherwise <code>None</code>.
197    *
198    * @return The success's value in <code>Some</code> if it exists, otherwise
199    * <code>None</code>
200    */
201   public abstract Option<A> toOption();
202 
203   private static final class Failure<A> extends Try<A> {
204 
205     private final Exception e;
206 
207     Failure(final Exception e) {
208       this.e = requireNonNull(e);
209     }
210 
211     @Override public <B> Try<B> map(final Function<? super A, ? extends B> f) {
212       return new Failure<>(e);
213     }
214 
215     @Override public boolean isFailure() {
216       return true;
217     }
218 
219     @Override public boolean isSuccess() {
220       return false;
221     }
222 
223     @Override public <B> Try<B> flatMap(final Function<? super A, Try<B>> f) {
224       return Try.failure(e);
225     }
226 
227     @Override public Try<A> recover(final Function<? super Exception, A> f) {
228       return Checked.of(() -> f.apply(e));
229     }
230 
231     @SuppressWarnings("unchecked") @Override public <X extends Exception> Try<A> recover(final Class<X> exceptionType, final Function<? super X, A> f) {
232       return exceptionType.isAssignableFrom(e.getClass()) ? Checked.of(() -> f.apply((X) e)) : this;
233     }
234 
235     @Override public Try<A> recoverWith(final Function<? super Exception, Try<A>> f) {
236       return f.apply(e);
237     }
238 
239     @SuppressWarnings("unchecked") @Override public <X extends Exception> Try<A> recoverWith(Class<X> exceptionType, Function<? super X, Try<A>> f) {
240       return exceptionType.isAssignableFrom(e.getClass()) ? f.apply((X) e) : this;
241     }
242 
243     @Override public A getOrElse(final Supplier<A> s) {
244       return s.get();
245     }
246 
247     @Override public <B> B fold(final Function<? super Exception, B> failureF, final Function<A, B> successF) {
248       return failureF.apply(e);
249     }
250 
251     @Override public Either<Exception, A> toEither() {
252       return Either.left(e);
253     }
254 
255     @Override public Option<A> toOption() {
256       return Option.none();
257     }
258 
259     @Override public boolean equals(final Object o) {
260       if (this == o) {
261         return true;
262       }
263       if (o == null || getClass() != o.getClass()) {
264         return false;
265       }
266 
267       final Failure<?> failure = (Failure<?>) o;
268 
269       return e != null ? e.equals(failure.e) : failure.e == null;
270     }
271 
272     @Override public int hashCode() {
273       return ~e.hashCode();
274     }
275   }
276 
277   private static final class Success<A> extends Try<A> {
278 
279     private final A value;
280 
281     Success(final A value) {
282       this.value = requireNonNull(value);
283     }
284 
285     @Override public <B> Try<B> map(final Function<? super A, ? extends B> f) {
286       return Checked.of(() -> f.apply(value));
287     }
288 
289     @Override public boolean isFailure() {
290       return false;
291     }
292 
293     @Override public boolean isSuccess() {
294       return true;
295     }
296 
297     @Override public <B> Try<B> flatMap(final Function<? super A, Try<B>> f) {
298       return f.apply(value);
299     }
300 
301     @Override public Try<A> recover(final Function<? super Exception, A> f) {
302       return this;
303     }
304 
305     @Override public <X extends Exception> Try<A> recover(final Class<X> exceptionType, final Function<? super X, A> f) {
306       return this;
307     }
308 
309     @Override public Try<A> recoverWith(final Function<? super Exception, Try<A>> f) {
310       return this;
311     }
312 
313     @Override public <X extends Exception> Try<A> recoverWith(final Class<X> exceptionType, final Function<? super X, Try<A>> f) {
314       return this;
315     }
316 
317     @Override public A getOrElse(final Supplier<A> s) {
318       return value;
319     }
320 
321     @Override public <B> B fold(final Function<? super Exception, B> failureF, final Function<A, B> successF) {
322       return successF.apply(value);
323     }
324 
325     @Override public Either<Exception, A> toEither() {
326       return Either.right(value);
327     }
328 
329     @Override public Option<A> toOption() {
330       return Option.some(value);
331     }
332 
333     @Override public boolean equals(final Object o) {
334       if (this == o) {
335         return true;
336       }
337       if (o == null || getClass() != o.getClass()) {
338         return false;
339       }
340       final Success<?> success = (Success<?>) o;
341       return value != null ? value.equals(success.value) : success.value == null;
342     }
343 
344     @Override public int hashCode() {
345       return value.hashCode();
346     }
347   }
348 }