Skip to content

Commit 2918835

Browse files
authored
Add Either.cond utility method for conditional Either creation (#3061)
Added support for `Either.cond`, similar to the one we can find in the Scala library: https://github.com/scala/scala/blob/6a23d33c53afa7b42f64bebaee9e0fc24479df6f/src/library/scala/util/Either.scala#L507
1 parent b41ffda commit 2918835

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

vavr/src/main/java/io/vavr/control/Either.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
* @param <L> The type of the Left value.
6464
* @param <R> The type of the Right value.
6565
*
66-
* @author Daniel Dietrich, Grzegorz Piwowarek
66+
* @author Daniel Dietrich, Grzegorz Piwowarek, Adam Kopeć
6767
*/
6868
public interface Either<L, R> extends Value<R>, Serializable {
6969

@@ -111,6 +111,50 @@ static <L, R> Either<L, R> narrow(Either<? extends L, ? extends R> either) {
111111
return (Either<L, R>) either;
112112
}
113113

114+
/**
115+
* Decides which {@code Either<L, R>} to return, depending on the test value -
116+
* if it's true, the result will be a {@link Either.Right},
117+
* if it's false - the result will be a {@link Either.Left}
118+
*
119+
* @param test A {@code boolean} value to evaluate
120+
* @param right A {@code Supplier<? extends R>} supplier of right value, called if test is true
121+
* @param left A {@code Supplier<? extends L>} supplier of left value, called if test is false
122+
* @param <L> Type of left value
123+
* @param <R> Type of right value
124+
*
125+
* @return {@code Either<L, R>} with right or left value, depending on the test condition evaluation
126+
*
127+
* @throws NullPointerException if any of the arguments is null
128+
*/
129+
static <L, R> Either<L, R> cond(boolean test, Supplier<? extends R> right, Supplier<? extends L> left) {
130+
Objects.requireNonNull(right, "right is null");
131+
Objects.requireNonNull(left, "left is null");
132+
133+
return test ? right(right.get()) : left(left.get());
134+
}
135+
136+
/**
137+
* Decides which {@code Either<L, R>} to return, depending on the test value -
138+
* if it's true, the result will be a {@link Either.Right},
139+
* if it's false - the result will be a {@link Either.Left}
140+
*
141+
* @param test A {@code boolean} value to evaluate
142+
* @param right A n{@code R} right value, returned if test is true
143+
* @param left A {@code L} left value, returned if test is false
144+
* @param <L> Type of left value
145+
* @param <R> Type of right value
146+
*
147+
* @return {@code Either<L, R>} with right or left value, depending on the test condition evaluation
148+
*
149+
* @throws NullPointerException if any of the arguments is null
150+
*/
151+
static <L, R> Either<L, R> cond(boolean test, R right, L left) {
152+
Objects.requireNonNull(right, "right is null");
153+
Objects.requireNonNull(left, "left is null");
154+
155+
return cond(test, () -> right, () -> left);
156+
}
157+
114158
/**
115159
* Returns the left value.
116160
*

vavr/src/test/java/io/vavr/control/EitherTest.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static io.vavr.API.Right;
3333
import static org.assertj.core.api.Assertions.assertThatThrownBy;
3434
import static org.junit.jupiter.api.Assertions.assertThrows;
35+
import static org.junit.jupiter.api.Assertions.fail;
3536

3637
@SuppressWarnings("deprecation")
3738
public class EitherTest extends AbstractValueTest {
@@ -333,6 +334,89 @@ public void shouldNarrowLeftEither() {
333334
}
334335
}
335336

337+
@Nested
338+
public class CondTests {
339+
340+
@Test
341+
public void shouldReturnRightIfTestTrue() {
342+
Either<String, Integer> either = Either.cond(true, () -> 21, () -> "vavr");
343+
assertThat(either).isEqualTo(Either.right(21));
344+
}
345+
346+
@Test
347+
public void shouldReturnLeftIfTestFalse() {
348+
Either<String, Integer> either = Either.cond(false, () -> 21, () -> "vavr");
349+
assertThat(either).isEqualTo(Either.left("vavr"));
350+
}
351+
352+
@Test
353+
public void shouldNotEvaluateRightSupplierOnFalse() {
354+
Either<String, Integer> either = Either.cond(false, () -> {
355+
fail("Should not be called");
356+
return 21;
357+
}, () -> "vavr");
358+
assertThat(either).isEqualTo(Either.left("vavr"));
359+
}
360+
361+
@Test
362+
public void shouldNotEvaluateLeftSupplierOnTrue() {
363+
Either<String, Integer> either = Either.cond(true, () -> 21, () -> {
364+
fail("Should not be called");
365+
return "vavr";
366+
});
367+
assertThat(either).isEqualTo(Either.right(21));
368+
}
369+
private class Animal {
370+
String name;
371+
Animal(String name) { this.name = name; }
372+
373+
@Override
374+
public boolean equals(Object o) {
375+
if (this == o) return true;
376+
if (!(o instanceof Animal)) return false;
377+
Animal other = (Animal) o;
378+
return name.equals(other.name);
379+
}
380+
381+
@Override
382+
public int hashCode() {
383+
return name.hashCode();
384+
}
385+
}
386+
387+
private class Dog extends Animal {
388+
Dog(String name) { super(name); }
389+
}
390+
391+
private class Cat extends Animal {
392+
Cat(String name) { super(name); }
393+
}
394+
395+
@Test
396+
public void shouldBeFineWithCovariantLeft() {
397+
Either<Animal, Integer> either = Either.cond(false, () -> 21, () -> new Cat("vavr"));
398+
assertThat(either).isEqualTo(Either.left(new Cat("vavr")));
399+
}
400+
401+
@Test
402+
public void shouldBeFineWithCovariantRight() {
403+
Either<String, Animal> either = Either.cond(true, () -> new Dog("vavr"), () -> "vavr");
404+
assertThat(either).isEqualTo(Either.right(new Dog("vavr")));
405+
}
406+
407+
@Test
408+
public void shouldMakeTheSameDecisionNoMatterHowItsCalled() {
409+
Either<String, Integer> e1 = Either.cond(true, () -> 21, () -> "vavr");
410+
Either<String, Integer> e2 = Either.cond(true, 21, "vavr");
411+
412+
Either<String, Integer> e3 = Either.cond(false, () -> 21, () -> "vavr");
413+
Either<String, Integer> e4 = Either.cond(false, 21, "vavr");
414+
415+
assertThat(List.of(e1, e2)).allMatch(e -> e.equals(Either.right(21)));
416+
assertThat(List.of(e3, e4)).allMatch(e -> e.equals(Either.left("vavr")));
417+
}
418+
}
419+
336420
@Nested
337421
public class OrElseTests {
338422

0 commit comments

Comments
 (0)