io.activej.common.collection.Either Maven / Gradle / Ivy
Show all versions of activej-common Show documentation
/*
* 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 extends L> 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 extends R> 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 super L> 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 super R> 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 super L, ? super R> 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 super L> leftConsumer, @NotNull Consumer super R> 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 super L, ? extends U> leftFn, @NotNull Function super R, ? extends U> 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 super L, ? super R, ? extends U> 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 super L, ? extends T> 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 super R, ? extends T> 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 super L, Either> 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 super R, Either> 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) + "}";
}
}