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.Function;
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(Function 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(Function> 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(Function)} 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(Function 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(Function)} 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(Function> 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());
}
}