The Either
type is closely related to Optional
, but can have different "failure" states, other than only empty.
Either
can be used to collect error messages in stream operations,
or simply as a lightweight alternative to throwing an Exception.
There are several popular libraries that offer an Either
type,
including vavr, fugue, and lambda.
This particular Either
is lightweight, and very easy to work with if you're already familiar with Optional
.
Sometimes, it can be desirable to put something into the "empty" value of an Optional
.
Let's call this "adding a Left value", since this value is no longer empty.
Going from Optional to Either is as easy as mapping with Either::right
,
followed by an orElseGet
to supply the Left value:
Either<String, BigInteger> possiblyPrime = Stream.generate(() ->
ThreadLocalRandom.current().nextInt(1000))
.map(BigInteger::valueOf)
.limit(10)
.filter(n -> n.isProbablePrime(10))
.findAny() // Optional<BigInteger>
.<Either<String, BigInteger>>map(Either::right) // Optional<Either<String, BigInteger>>
.orElseGet(() -> Either.left("no such value")); // Either<String, BigInteger>
Repeating the result type in the penultimate line is necessary, due to a limitation of Java's typechecker.
Either
has the familiar methods from Optional
: map
, flatMap
and filter
.
These will always return a Left value unchanged,
just like the corresponding methods in Optional
, which return an empty value unchanged.
Symmetrically there are mapLeft
, flatMapLeft
and filterLeft
, which return a Right value unchanged.
Finally there is ifPresentOrElse ifLeftOrElse
(1.3) and the all-powerful fold
method,
as well as getRight
and getLeft
to convert back to Optional
.
If you have a stream of Either
, you can search for Left values with custom collectors
firstFailure
or allFailures
:
Either<BigInteger, List<BigInteger>> twoPrimesOrOneComposite = Stream.generate(() ->
ThreadLocalRandom.current().nextInt(1000))
.map(BigInteger::valueOf)
.limit(2)
.<Either<BigInteger, BigInteger>>map(n -> n.isProbablePrime(10) ?
Either.right(n) : Either.left(n))
.collect(Eithers.firstFailure());
This library grew for several months as part of the jbock project,
until it was released independently. jbock uses it internally to perform input validation,
and its generated parse
method returns an Either
.