Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion vavr/src/main/java/io/vavr/control/Either.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
* @param <L> The type of the Left value.
* @param <R> The type of the Right value.
*
* @author Daniel Dietrich, Grzegorz Piwowarek
* @author Daniel Dietrich, Grzegorz Piwowarek, Adam Kopeć
*/
public interface Either<L, R> extends Value<R>, Serializable {

Expand Down Expand Up @@ -111,6 +111,50 @@ static <L, R> Either<L, R> narrow(Either<? extends L, ? extends R> either) {
return (Either<L, R>) either;
}

/**
* Decides which {@code Either<L, R>} 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<? extends R>} supplier of right value, called if test is true
* @param left A {@code Supplier<? extends L>} supplier of left value, called if test is false
* @param <L> Type of left value
* @param <R> Type of right value
*
* @return {@code Either<L, R>} with right or left value, depending on the test condition evaluation
*
* @throws NullPointerException if any of the arguments is null
*/
static <L, R> Either<L, R> cond(boolean test, Supplier<? extends R> right, Supplier<? extends L> 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<L, R>} 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 <L> Type of left value
* @param <R> Type of right value
*
* @return {@code Either<L, R>} with right or left value, depending on the test condition evaluation
*
* @throws NullPointerException if any of the arguments is null
*/
static <L, R> Either<L, R> 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.
*
Expand Down
84 changes: 84 additions & 0 deletions vavr/src/test/java/io/vavr/control/EitherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -333,6 +334,89 @@ public void shouldNarrowLeftEither() {
}
}

@Nested
public class CondTests {

@Test
public void shouldReturnRightIfTestTrue() {
Either<String, Integer> either = Either.cond(true, () -> 21, () -> "vavr");
assertThat(either).isEqualTo(Either.right(21));
}

@Test
public void shouldReturnLeftIfTestFalse() {
Either<String, Integer> either = Either.cond(false, () -> 21, () -> "vavr");
assertThat(either).isEqualTo(Either.left("vavr"));
}

@Test
public void shouldNotEvaluateRightSupplierOnFalse() {
Either<String, Integer> either = Either.cond(false, () -> {
fail("Should not be called");
return 21;
}, () -> "vavr");
assertThat(either).isEqualTo(Either.left("vavr"));
}

@Test
public void shouldNotEvaluateLeftSupplierOnTrue() {
Either<String, Integer> 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<Animal, Integer> either = Either.cond(false, () -> 21, () -> new Cat("vavr"));
assertThat(either).isEqualTo(Either.left(new Cat("vavr")));
}

@Test
public void shouldBeFineWithCovariantRight() {
Either<String, Animal> either = Either.cond(true, () -> new Dog("vavr"), () -> "vavr");
assertThat(either).isEqualTo(Either.right(new Dog("vavr")));
}

@Test
public void shouldMakeTheSameDecisionNoMatterHowItsCalled() {
Either<String, Integer> e1 = Either.cond(true, () -> 21, () -> "vavr");
Either<String, Integer> e2 = Either.cond(true, 21, "vavr");

Either<String, Integer> e3 = Either.cond(false, () -> 21, () -> "vavr");
Either<String, Integer> 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 {

Expand Down