All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.activej.common.collection.Try Maven / Gradle / Ivy

There is a newer version: 6.0-rc1
Show newest version
/*
 * Copyright (C) 2020 ActiveJ LLC.
 *
 * 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 io.activej.common.collection;

import io.activej.common.function.FunctionEx;
import io.activej.common.function.RunnableEx;
import io.activej.common.function.SupplierEx;
import io.activej.common.recycle.Recyclers;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.*;

import static io.activej.common.Checks.checkArgument;
import static io.activej.common.Checks.checkState;
import static io.activej.common.exception.FatalErrorHandlers.handleError;

/**
 * A compound type that represents either a result or an exception
 * 

* Semantically close to {@code Either} * * @param type of result * @see Either */ public final class Try { static { Recyclers.register(Try.class, aTry -> Recyclers.recycle(aTry.result)); } private final T result; private final @Nullable Exception exception; private Try(@Nullable T result, @Nullable Exception e) { this.result = result; this.exception = e; } /** * Creates a new successful {@link Try} with a result * * @param result a result value of a {@link Try} * @param a type of result * @return a new instance of a successful {@link Try} */ public static Try of(@Nullable T result) { return new Try<>(result, null); } /** * Creates a new {@link Try} which is either successful * and has a result or is failed and has an exception * * @param result a result value of a {@link Try} * @param e an exception of a {@link Try} * @param a type of result * @return a new instance of a {@link Try} that is either successful or failed */ public static Try of(@Nullable T result, @Nullable Exception e) { checkArgument(result == null || e == null, "Either result or exception should be null"); return new Try<>(result, e); } /** * Creates a new failed {@link Try} with a given exception * * @param e an exception of a {@link Try} * @param a type of result * @return a new instance of a failed {@link Try} */ public static Try ofException(@NotNull Exception e) { return new Try<>(null, e); } /** * Creates a new {@link Try} which is either successful or failed * based on the result of a supplier call *

* If supplier throws exception a {@link Try} is failed. * Otherwise, it will have supplied value as a result value of a {@link Try} * * @param computation a throwing supplier of a result of a {@link Try} * @param a type of result * @return a new instance of a {@link Try} that is either successful or failed */ public static Try wrap(@NotNull SupplierEx computation) { try { return new Try<>(computation.get(), null); } catch (Exception ex) { handleError(ex, computation); return new Try<>(null, ex); } } /** * Creates a new {@link Try} which is either successful or failed * based on the result of a runnable call *

* If runnable throws exception a {@link Try} is failed. * Otherwise, it will have {@code null} as a result value of a {@link Try} * * @param computation a throwing runnable * @param a type of result * @return a new instance of a {@link Try} that is either successful or failed */ public static Try wrap(@NotNull RunnableEx computation) { try { computation.run(); return new Try<>(null, null); } catch (Exception ex) { handleError(ex, computation); return new Try<>(null, ex); } } /** * Creates a new {@link Try} which is either successful or failed * based on the result of a callable call *

* If callable throws a runtime exception a {@link Try} is failed. * Otherwise, it will have supplied value as a result value of a {@link Try} * * @param computation a callable of a result of a {@link Try} * @param a type of result * @return a new instance of a {@link Try} that is either successful or failed */ public static Try wrap(@NotNull Callable computation) { try { @Nullable T result = computation.call(); return new Try<>(result, null); } catch (Exception e) { handleError(e, computation); return new Try<>(null, e); } } /** * Returns whether this {@link Try} is successful */ @Contract(pure = true) public boolean isSuccess() { return exception == null; } /** * Returns whether this {@link Try} is failed */ @Contract(pure = true) public boolean isException() { return exception != null; } /** * Returns a result of this {@link Try}, possibly {@code null} */ @Contract(pure = true) public @Nullable T get() { return result; } /** * Returns a result of this {@link Try} if a {@link Try} is successful. * Otherwise, returns a given default value * * @param defaultValue a default value to be returned if this {@link Try} is failed * @return a result of this {@link Try} or a given default value */ @Contract(pure = true) public T getElse(@Nullable T defaultValue) { return exception == null ? result : defaultValue; } /** * Returns a result of this {@link Try} if a {@link Try} is successful. * Otherwise, returns a supplied default value * * @param defaultValueSupplier a supplier of a default value to be returned if this {@link Try} is failed * @return a result of this {@link Try} or a supplied default value */ @Contract(pure = true) public T getElseGet(@NotNull Supplier defaultValueSupplier) { return exception == null ? result : defaultValueSupplier.get(); } /** * Returns an exception of this {@link Try}, possibly {@code null} */ @Contract(pure = true) public @Nullable Exception getException() { return exception; } /** * Consumes a result of this {@link Try} if it is successful. * Otherwise, does nothing *

* Always returns this {@link Try} * * @param resultConsumer a consumer of a result * @return this {@link Try} */ public @NotNull Try ifSuccess(@NotNull Consumer resultConsumer) { if (isSuccess()) { resultConsumer.accept(result); } return this; } /** * Consumes an exception of this {@link Try} if it is failed. * Otherwise, does nothing *

* Always returns this {@link Try} * * @param exceptionConsumer a consumer of an exception * @return this {@link Try} */ public @NotNull Try ifException(@NotNull Consumer exceptionConsumer) { if (isException()) { exceptionConsumer.accept(exception); } return this; } /** * Consumes both a result and an exception of this {@link Try}. *

* Always returns this {@link Try} * * @param consumer a consumer of a result and an exception * @return this {@link Try} */ public @NotNull Try consume(@NotNull BiConsumer consumer) { consumer.accept(result, exception); return this; } /** * Consumes a result of this {@link Try} if it is successful. * Otherwise, consumes an exception *

* Always returns this {@link Try} * * @param resultConsumer a consumer of a result * @param exceptionConsumer a consumer of an exception * @return this {@link Try} */ public @NotNull Try consume(@NotNull Consumer resultConsumer, @NotNull Consumer exceptionConsumer) { if (isSuccess()) { resultConsumer.accept(result); } else { exceptionConsumer.accept(exception); } return this; } /** * Transforms a failed {@link Try} into a failed {@link Try} * * @param a result type of new failed {@link Try} * @return a molded {@link Try} */ @SuppressWarnings("unchecked") @Contract(pure = true) private @NotNull Try mold() { checkState(isException()); return (Try) this; } /** * Applies a function to either this {@link Try}'s result or an exception * * @param function a function to map a result value * @param exceptionFunction a function to map an exception * @param a type of mapping result * @return a result of mapping of either a result or an exception of a {@link Try} */ @Contract(pure = true) public U reduce(@NotNull Function function, @NotNull Function<@NotNull Exception, ? extends U> exceptionFunction) { return exception == null ? function.apply(result) : exceptionFunction.apply(exception); } /** * Applies a function to this {@link Try}'s result and an exception * * @param fn a function to map a result value and an exception * @param a type of mapping result * @return a result of mapping of a result and an exception of a {@link Try} */ @Contract(pure = true) public U reduce(@NotNull BiFunction fn) { return fn.apply(result, exception); } /** * Returns a mapped {@link Try} obtained by mapping a result * of this {@link Try} to a new value * * @param function a function to map left value to a new value * @param a type of result of a new {@link Try} * @return a {@link Try} with a mapped result */ @Contract(pure = true) public @NotNull Try map(@NotNull FunctionEx function) { if (exception == null) { try { return new Try<>(function.apply(result), null); } catch (Exception ex) { handleError(ex, function); return new Try<>(null, ex); } } return mold(); } /** * Returns a mapped {@link Try} obtained by mapping a result * of this {@link Try} to a new {@link Try} * * @param function a function to map a result of this {@link Try} a new {@link Try} * @param a type of result of a new {@link Try} * @return a mapped {@link Try} */ @Contract(pure = true) public @NotNull Try flatMap(@NotNull Function> function) { return exception == null ? function.apply(result) : mold(); } /** * Convert this {@link Try} to an {@code Either} * * @return an {@link Either} which represents either a result or an exception of this {@link Try} */ @Contract(pure = true) public @NotNull Either toEither() { return exception == null ? Either.left(result) : Either.right(exception); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Try other = (Try) o; if (!Objects.equals(result, other.result)) return false; return Objects.equals(exception, other.exception); } @Override public int hashCode() { int hash = result != null ? result.hashCode() : 0; hash = 31 * hash + (exception != null ? exception.hashCode() : 0); return hash; } @Override public String toString() { return "{" + (isSuccess() ? "" + result : "" + exception) + "}"; } }