Just like java.util.Optional in Java 8 is (somewhat) equivalent to Scala\'s Option[T] type, is there an equivalent to Scala\'s Eithe
There is no Either type is Java 8, so you need to create one yourself or use some third-party library.
You may build such a feature using the new Optional type (but read to the end of this answer):
final class Either
{
public static Either left(L value) {
return new Either<>(Optional.of(value), Optional.empty());
}
public static Either right(R value) {
return new Either<>(Optional.empty(), Optional.of(value));
}
private final Optional left;
private final Optional right;
private Either(Optional l, Optional r) {
left=l;
right=r;
}
public T map(
Function super L, ? extends T> lFunc,
Function super R, ? extends T> rFunc)
{
return left.map(lFunc).orElseGet(()->right.map(rFunc).get());
}
public Either mapLeft(Function super L, ? extends T> lFunc)
{
return new Either<>(left.map(lFunc),right);
}
public Either mapRight(Function super R, ? extends T> rFunc)
{
return new Either<>(left, right.map(rFunc));
}
public void apply(Consumer super L> lFunc, Consumer super R> rFunc)
{
left.ifPresent(lFunc);
right.ifPresent(rFunc);
}
}
Example use case:
new Random().ints(20, 0, 2).mapToObj(i -> (Either)(i==0?
Either.left("left value (String)"):
Either.right(42)))
.forEach(either->either.apply(
left ->{ System.out.println("received left value: "+left.substring(11));},
right->{ System.out.println("received right value: 0x"+Integer.toHexString(right));}
));
In retrospective, the Optional based solution is more like an academic example, but not a recommended approach. One problem is the treatment of null as “empty” which contradicts the meaning of “either”.
The following code shows an Either that considers null a possible value, so it’s strictly “either”, left or right, even if the value is null:
abstract class Either
{
public static Either left(L value) {
return new Either() {
@Override public T map(Function super L, ? extends T> lFunc,
Function super R, ? extends T> rFunc) {
return lFunc.apply(value);
}
};
}
public static Either right(R value) {
return new Either() {
@Override public T map(Function super L, ? extends T> lFunc,
Function super R, ? extends T> rFunc) {
return rFunc.apply(value);
}
};
}
private Either() {}
public abstract T map(
Function super L, ? extends T> lFunc, Function super R, ? extends T> rFunc);
public Either mapLeft(Function super L, ? extends T> lFunc) {
return this.>map(t -> left(lFunc.apply(t)), t -> (Either)this);
}
public Either mapRight(Function super R, ? extends T> lFunc) {
return this.>map(t -> (Either)this, t -> right(lFunc.apply(t)));
}
public void apply(Consumer super L> lFunc, Consumer super R> rFunc) {
map(consume(lFunc), consume(rFunc));
}
private Function consume(Consumer c) {
return t -> { c.accept(t); return null; };
}
}
It’s easy to change that to a strict rejection of null by simply inserting an Objects.requireNonNull(value) at the beginning of both factory methods. Likewise, adding support for an empty either would be imaginable.