javascalautils.Try Maven / Gradle / Ivy
/**
* Copyright 2015 Peter Nerg
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
*
Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package javascalautils;
import java.util.Iterator;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* The Try type represents a computation that may either result in an exception, or return a
* successfully computed value.
* Typical use case is situations where parallel computation takes place resulting in more than one
* response where it's possible that one or more computations fail.
* Though it might not be desirable to raise an exception for failed computations hence the Try acts
* a place holder for a response that is either failed or successful.
* Instances of Try, are either an instance of {@link Success} or {@link Failure}.
* Example of usage:
*
*
*
*
* Try<SomeData> getSomeData(SomeInput input) {
* try {
* // readUserFromDB throws if no user exists
* SomeData data = readUserFromDB(input);
* return new Success<>(data);
* } catch (SomeException ex) {
* return new Failure<>(ex);
* }
* }
*
*
*
*
* A more elaborate way is to provide a {@link ThrowableFunction0 checked function} provided to the
* {@link #apply(ThrowableFunction0)} method.
*
*
*
*
* Try<Integer> resultSuccess = Try.apply(() -> 9 / 3); // results in Success(3)
* Try<Integer> resultFailure = Try.apply(() -> 9 / 0); // results in Failure(ArithmeticException)
*
*
*
*
* Or let us re-write the first example to use the {@link #apply(ThrowableFunction0)} method.
*
*
*
*
* Try<SomeData> getSomeData(SomeInput input) {
* // readUserFromDB throws if no user exists
* Try.apply(() -> readUserFromDB(input));
* }
*
*
*
*
* To make it even more concise we can create Try instances just by using {@link
* TryCompanion#Try(ThrowableFunction0) Try(ThrowableFunction0)}.
* This however requires you to statically import the proper methods from the {@link TryCompanion
* companion class} related to Try.
*
*
*
*
* import static javascalautils.TryCompanion.Try;
*
* Try<SomeData> getSomeData(SomeInput input) {
* // readUserFromDB throws if no user exists
* Try(() -> readUserFromDB(input));
* }
*
*
*
*
* @author Peter Nerg
* @since 1.0
* @param The type of the value represented by this Try
*/
public interface Try extends Iterable {
/**
* Creates an instance of Try.
* If a null
or non-throwable value is provided then {@link Success} is returned
* containing the value, else {@link Failure} containing the provided throwable.
*
* @param The type for the Try
* @param value The value for this to create a Try
* @return The Try instance
* @since 1.0
*/
static Try apply(T value) {
return value instanceof Throwable ? new Failure<>((Throwable) value) : new Success<>(value);
}
/**
* Creates an instance of Try wrapping the result of the provided function.
* If the function results in a value then {@link Success} with the value is returned.
* In case the function raises an exception then {@link Failure} is returned containing that
* exception.
* If null
is provided as argument then the {@link #apply(Object)} is invoked.
* Example simple division by zero results in an exception.
*
*
*
*
* Try<Integer> resultSuccess = Try.apply(() -> 9 / 3); // results in Success(3)
* Try<Integer> resultFailure = Try.apply(() -> 9 / 0); // results in Failure(ArithmeticException)
*
*
*
*
* @param The type for the Try
* @param function The function to render either the value T or raise an exception.
* @return The resulting Try instance wrapping what the function resulted in
* @since 1.3
*/
static Try apply(ThrowableFunction0 function) {
if (function == null) {
return apply((T) null);
}
try {
return new Success<>(function.apply());
} catch (Throwable ex) {
return new Failure<>(ex);
}
}
/**
* Returns true
if the Try is a {@link Failure}, false
otherwise.
*
* @return If the Try is a {@link Failure}
* @since 1.0
*/
default boolean isFailure() {
return !isSuccess();
}
/**
* Returns true
if the Try is a {@link Success}, false
otherwise.
*
* @return If the Try is a {@link Success}
* @since 1.0
*/
boolean isSuccess();
/**
* Returns the value from this {@link Success} or the value provided by the supplier if this is a
* {@link Failure}.
*
* @param supplier The supplier to return the value in case of a {@link Failure}
* @return The value from the Try or the supplier
* @since 1.0
*/
T getOrElse(Supplier supplier);
/**
* Returns the value if it is a {@link Success}, else null
.
*
* @return The value of the Try or null
.
* @since 1.0
*/
default T orNull() {
return getOrElse(() -> null);
}
/**
* Returns this Try if it's a {@link Success} or the value provided by the supplier if this
* is a {@link Failure}.
*
* @param supplier The supplier to return the value in case of a {@link Failure}
* @return This try or the value from the supplier
*/
Try orElse(Supplier> supplier);
/**
* Returns the value from this {@link Success} or throws the exception if this is a {@link
* Failure}.
*
* @return The value of the {@link Success}
* @throws Throwable The Throwable in case of a {@link Failure}
* @since 1.0
*/
T get() throws Throwable;
/**
* Completes this Try with an exception wrapped in a {@link Success}.
* The exception is either the exception that the Try failed with (if a {@link Failure}) or
* an 'UnsupportedOperationException'.
*
* @return The value of the {@link Failure} in a {@link Success}
* @since 1.0
*/
Try failed();
/**
* Applies the predicate to the value of the {@link Try} and either returns the Try if the
* predicate matched or a {@link Failure}.
* One of the three outcomes are applicable:
*
*
* - Instance is {@link Success} and predicate matches -> return this
*
- Instance is {@link Success} and predicate does not match -> return {@link Failure}
*
- Instance is {@link Failure} -> return this
*
-
*
*
* @param filter The filter to apply
* @return The try matching the filter, either this if matching or a {@link Failure} in
* case no match
* @since 1.4
*/
Try filter(Predicate filter);
/**
* Returns the Try's value in an {@link Iterator} if it is a {@link Success}, or an empty {@link
* Iterator} if it is Failure.
*
* @return The iterator for the Try
* @since 1.0
*/
Iterator iterator();
/**
* Maps the given function to the value from this {@link Success} or returns this if this
* is a {@link Failure}.
* This allows for mapping a {@link Try} containing some type to some completely different type.
*
* The example converts a {@link Try} of type String to Integer.
*
*
*
*
* Try<String> t = ...
* Try<Integer> t2 = t.map(v -> v.length);
*
*
*
*
* @param The type for the return value from the function
* @param function The function to use
* @return The Option containing the mapped value
* @since 1.0
*/
Try map(ThrowableFunction1 function);
/**
* Maps the given function to the value from this {@link Success} or returns this if this
* is a {@link Failure}.
*
* @param The type for the return value from the function
* @param function The function to use
* @return The Option containing the mapped value
* @since 1.2
*/
Try flatMap(ThrowableFunction1> function);
/**
* Creates a new {@link Try} that in case this {@link Try} is a {@link Failure} will apply
* the function to recover to a {@link Success}.
* Should this be a {@link Success} then this is returned, i.e. no new instance is
* created.
* This is a kind of {@link #map(ThrowableFunction1)} for failures only.
* E.g.
* In case of t being successful then that value is passed on to recovered, in case
* of failure then the recover function kicks in and returns the message from the throwable.
*
*
*
*
* Try<String> t = ...
* Try<String> recovered = t.recover(t -> t.getMessage());
*
*
*
*
*
* This statement will be Success(3)
*
*
*
* Try<Integer> t = Try(() -> 9/3).recover(t -> 0)
*
*
*
*
* Whilst this statement will be Success(0)
as it is division-by-zero
*
*
*
* Try<Integer> t = Try(() -> 9/0).recover(t -> 0)
*
*
*
* @param function The function to apply in case of a {@link Failure}
* @return The recovered Try
* @since 1.4
*/
Try recover(ThrowableFunction1 function);
/**
* Creates a new {@link Try} that in case this {@link Try} is a {@link Failure} will apply
* the function to recover the {@link Try} rendered by the function.
* Should this be a {@link Success} the value is propagated as-is.
* This is a kind of {@link #map(ThrowableFunction1)} for failures only.
* E.g.
*
*
*
*
* Try<String> t = ...
* Try<String> recovered = t.recover(t -> new Success<>(t.getMessage()));
*
*
*
*
* In case of t being successful then that value is passed on to recovered, in case
* of failure then the recover function kicks in and returns the a {@link Success} with message
* from the throwable.
*
* @param function The function to apply in case of a {@link Failure}
* @return The recovered Try
* @since 1.4
*/
Try recoverWith(ThrowableFunction1> function);
/**
* Returns the Try's value in a Stream if it is a {@link Success}, or an empty Stream if it is a
* {@link Failure}.
*
* @return The stream for the Try
* @since 1.0
*/
Stream stream();
/**
* Returns this {@link Try} as an {@link Option}.
* If it is a {@link Success} then the value is wrapped in {@link Some} else {@link None} is
* returned.
* Should the {@link Success} contain a null
value the result will be {@link None} as
* null
values are per definition none/nothing.
*
* @return The {@link Option} representing this Try
* @since 1.0
*/
default Option asOption() {
return Option.apply(orNull());
}
}