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

io.activej.common.collection.Either 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.recycle.Recyclers;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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

/**
 * A compound type that represents a disjoint union of two values (only one value is present, either left or right)
 * 

* Supports {@code null} values * * @param type of left value * @param type of right value */ public final class Either { static { Recyclers.register(Either.class, either -> { Recyclers.recycle(either.left); Recyclers.recycle(either.right); }); } private final @Nullable L left; private final @Nullable R right; private final boolean isRight; // so that this either supports nulls private Either(@Nullable L left, @Nullable R right, boolean isRight) { this.left = left; this.right = right; this.isRight = isRight; } /** * Creates a new {@link Either} with a left value * * @param left an object to be used as a left value * @param type of left value * @param type of right value * @return a new instance of {@link Either} with a left value */ public static Either left(@Nullable L left) { return new Either<>(left, null, false); } /** * Creates a new {@link Either} with a right value * * @param right an object to be used as a right value * @param type of left value * @param type of right value * @return a new instance of {@link Either} with a right value */ public static Either right(@Nullable R right) { return new Either<>(null, right, true); } /** * Returns whether this {@link Either} is left (has left value) */ @Contract(pure = true) public boolean isLeft() { return !isRight; } /** * Returns whether this {@link Either} is right (has right value) */ @Contract(pure = true) public boolean isRight() { return isRight; } /** * Returns a left value */ @Contract(pure = true) public @Nullable L getLeft() { return left; } /** * Returns a right value */ @Contract(pure = true) public @Nullable R getRight() { return right; } /** * If this {@link Either} is left, returns left value. * Otherwise, returns a given default value * * @param defaultValue a default value to be returned if this {@link Either} is right * @return a left value if this {@link Either} is left or a default value, otherwise */ @Contract(pure = true) public L getLeftElse(@Nullable L defaultValue) { return isLeft() ? left : defaultValue; } /** * If this {@link Either} is right, returns right value. * Otherwise, returns a given default value * * @param defaultValue a default value to be returned if this {@link Either} is left * @return a right value if this {@link Either} is right or a default value, otherwise */ @Contract(pure = true) public R getRightElse(@Nullable R defaultValue) { return isRight() ? right : defaultValue; } /** * If this {@link Either} is left, returns left value. * Otherwise, returns a supplied default value * * @param defaultValueSupplier a supplier of default value to be obtained if this {@link Either} is right * @return a left value if this {@link Either} is left or a supplied default value, otherwise */ @Contract(pure = true) public L getLeftElseGet(@NotNull Supplier defaultValueSupplier) { return isLeft() ? left : defaultValueSupplier.get(); } /** * If this {@link Either} is right, returns right value. * Otherwise, returns a supplied default value * * @param defaultValueSupplier a supplier of default value to be obtained if this {@link Either} is left * @return a right value if this {@link Either} is right or a supplied default value, otherwise */ @Contract(pure = true) public R getRightElseGet(@NotNull Supplier defaultValueSupplier) { return isRight() ? right : defaultValueSupplier.get(); } /** * Consumes a left value if this {@link Either} is left. * Otherwise, does nothing *

* Always returns this {@link Either} * * @param leftConsumer a consumer of left value * @return this {@link Either} */ @Contract(pure = true) public @NotNull Either ifLeft(@NotNull Consumer leftConsumer) { if (isLeft()) { leftConsumer.accept(left); } return this; } /** * Consumes a right value if this {@link Either} is right. * Otherwise, does nothing *

* Always returns this {@link Either} * * @param rightConsumer a consumer of right value * @return this {@link Either} */ @Contract(pure = true) public @NotNull Either ifRight(@NotNull Consumer rightConsumer) { if (isRight()) { rightConsumer.accept(right); } return this; } /** * Consumes both left and right values *

* Always returns this {@link Either} * * @param consumer a consumer of left and right values * @return this {@link Either} */ @Contract(pure = true) public @NotNull Either consume(@NotNull BiConsumer consumer) { consumer.accept(left, right); return this; } /** * Consumes both left and right values *

* Always returns this {@link Either} * * @param leftConsumer a consumer of left value * @param rightConsumer a consumer of right value * @return this {@link Either} */ @Contract(pure = true) public @NotNull Either consume(@NotNull Consumer leftConsumer, @NotNull Consumer rightConsumer) { if (isLeft()) { leftConsumer.accept(left); } else { rightConsumer.accept(right); } return this; } /** * Applies a function to this {@link Either}'s left or right value * * @param leftFn a function to map left value * @param rightFn a function to map left value * @param a type of mapping result * @return a result of mapping of either left or right value */ @Contract(pure = true) public U reduce(@NotNull Function leftFn, @NotNull Function rightFn) { return isLeft() ? leftFn.apply(left) : rightFn.apply(right); } /** * Applies a function to this {@link Either}'s left and right values * * @param fn a function to map left and right values * @param a type of mapping result * @return a result of mapping of left and right values */ @Contract(pure = true) public U reduce(@NotNull BiFunction fn) { return fn.apply(left, right); } /** * Returns a new {@link Either} which has its left and right values swapped * * @return a swapped {@link Either} */ @Contract(pure = true) public @NotNull Either swap() { return new Either<>(right, left, !isRight); } /** * Returns a mapped {@link Either} obtained by mapping a left value * of this {@link Either} to a new value * * @param fn a function to map left value to a new value * @param a type of new left value * @return an {@link Either} with a mapped left value */ @SuppressWarnings("unchecked") @Contract(pure = true) public @NotNull Either mapLeft(@NotNull Function fn) { return isLeft() ? new Either<>(fn.apply(left), null, false) : (Either) this; } /** * Returns a mapped {@link Either} obtained by mapping a right value * of this {@link Either} to a new value * * @param fn a function to map right value to a new value * @param a type of new right value * @return an {@link Either} with a mapped right value */ @SuppressWarnings("unchecked") @Contract(pure = true) public @NotNull Either mapRight(@NotNull Function fn) { return isRight() ? new Either<>(null, fn.apply(right), true) : (Either) this; } /** * Returns a mapped {@link Either} obtained by mapping a left value * of this {@link Either} to a new {@link Either} which has the same right type * * @param fn a function to map left value to a new {@link Either} * @param a type of new left value * @return a mapped {@link Either} */ @SuppressWarnings("unchecked") @Contract(pure = true) public @NotNull Either flatMapLeft(@NotNull Function> fn) { return isLeft() ? fn.apply(left) : (Either) this; } /** * Returns a mapped {@link Either} obtained by mapping a right value * of this {@link Either} to a new {@link Either} which has the same left type * * @param fn a function to map right value to a new {@link Either} * @param a type of new right value * @return a mapped {@link Either} */ @SuppressWarnings("unchecked") @Contract(pure = true) public @NotNull Either flatMapRight(@NotNull Function> fn) { return isRight() ? fn.apply(right) : (Either) this; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Either either = (Either) o; return Objects.equals(left, either.left) && Objects.equals(right, either.right); } @Override public int hashCode() { int result = 0; result = 31 * result + (left != null ? left.hashCode() : 0); result = 31 * result + (right != null ? right.hashCode() : 0); return result; } @Override public String toString() { return "{" + (isLeft() ? "left=" + left : "right=" + right) + "}"; } }