diff --git a/vavr/src/main/java/io/vavr/control/Either.java b/vavr/src/main/java/io/vavr/control/Either.java index 93129ecf54..99b0f08a80 100644 --- a/vavr/src/main/java/io/vavr/control/Either.java +++ b/vavr/src/main/java/io/vavr/control/Either.java @@ -63,7 +63,7 @@ * @param The type of the Left value. * @param The type of the Right value. * - * @author Daniel Dietrich, Grzegorz Piwowarek + * @author Daniel Dietrich, Grzegorz Piwowarek, Adam Kopeć */ public interface Either extends Value, Serializable { @@ -111,6 +111,50 @@ static Either narrow(Either either) { return (Either) either; } + /** + * Decides which {@code Either} to return, depending on the test value - + * if it's true, the result will be a {@link Either.Right}, + * if it's false - the result will be a {@link Either.Left} + * + * @param test A {@code boolean} value to evaluate + * @param right A {@code Supplier} supplier of right value, called if test is true + * @param left A {@code Supplier} supplier of left value, called if test is false + * @param Type of left value + * @param Type of right value + * + * @return {@code Either} with right or left value, depending on the test condition evaluation + * + * @throws NullPointerException if any of the arguments is null + */ + static Either cond(boolean test, Supplier right, Supplier left) { + Objects.requireNonNull(right, "right is null"); + Objects.requireNonNull(left, "left is null"); + + return test ? right(right.get()) : left(left.get()); + } + + /** + * Decides which {@code Either} to return, depending on the test value - + * if it's true, the result will be a {@link Either.Right}, + * if it's false - the result will be a {@link Either.Left} + * + * @param test A {@code boolean} value to evaluate + * @param right A n{@code R} right value, returned if test is true + * @param left A {@code L} left value, returned if test is false + * @param Type of left value + * @param Type of right value + * + * @return {@code Either} with right or left value, depending on the test condition evaluation + * + * @throws NullPointerException if any of the arguments is null + */ + static Either cond(boolean test, R right, L left) { + Objects.requireNonNull(right, "right is null"); + Objects.requireNonNull(left, "left is null"); + + return cond(test, () -> right, () -> left); + } + /** * Returns the left value. * diff --git a/vavr/src/test/java/io/vavr/control/EitherTest.java b/vavr/src/test/java/io/vavr/control/EitherTest.java index 63a05f5ba9..b1b2b46507 100644 --- a/vavr/src/test/java/io/vavr/control/EitherTest.java +++ b/vavr/src/test/java/io/vavr/control/EitherTest.java @@ -32,6 +32,7 @@ import static io.vavr.API.Right; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; @SuppressWarnings("deprecation") public class EitherTest extends AbstractValueTest { @@ -333,6 +334,89 @@ public void shouldNarrowLeftEither() { } } + @Nested + public class CondTests { + + @Test + public void shouldReturnRightIfTestTrue() { + Either either = Either.cond(true, () -> 21, () -> "vavr"); + assertThat(either).isEqualTo(Either.right(21)); + } + + @Test + public void shouldReturnLeftIfTestFalse() { + Either either = Either.cond(false, () -> 21, () -> "vavr"); + assertThat(either).isEqualTo(Either.left("vavr")); + } + + @Test + public void shouldNotEvaluateRightSupplierOnFalse() { + Either either = Either.cond(false, () -> { + fail("Should not be called"); + return 21; + }, () -> "vavr"); + assertThat(either).isEqualTo(Either.left("vavr")); + } + + @Test + public void shouldNotEvaluateLeftSupplierOnTrue() { + Either either = Either.cond(true, () -> 21, () -> { + fail("Should not be called"); + return "vavr"; + }); + assertThat(either).isEqualTo(Either.right(21)); + } + private class Animal { + String name; + Animal(String name) { this.name = name; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Animal)) return false; + Animal other = (Animal) o; + return name.equals(other.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + } + + private class Dog extends Animal { + Dog(String name) { super(name); } + } + + private class Cat extends Animal { + Cat(String name) { super(name); } + } + + @Test + public void shouldBeFineWithCovariantLeft() { + Either either = Either.cond(false, () -> 21, () -> new Cat("vavr")); + assertThat(either).isEqualTo(Either.left(new Cat("vavr"))); + } + + @Test + public void shouldBeFineWithCovariantRight() { + Either either = Either.cond(true, () -> new Dog("vavr"), () -> "vavr"); + assertThat(either).isEqualTo(Either.right(new Dog("vavr"))); + } + + @Test + public void shouldMakeTheSameDecisionNoMatterHowItsCalled() { + Either e1 = Either.cond(true, () -> 21, () -> "vavr"); + Either e2 = Either.cond(true, 21, "vavr"); + + Either e3 = Either.cond(false, () -> 21, () -> "vavr"); + Either e4 = Either.cond(false, 21, "vavr"); + + assertThat(List.of(e1, e2)).allMatch(e -> e.equals(Either.right(21))); + assertThat(List.of(e3, e4)).allMatch(e -> e.equals(Either.left("vavr"))); + } + } + @Nested public class OrElseTests {