92

Just like java.util.Optional<T> in Java 8 is (somewhat) equivalent to Scala's Option[T] type, is there an equivalent to Scala's Either[L, R]?

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
HRJ
  • 17,079
  • 11
  • 56
  • 80

8 Answers8

80

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<L,R>
{
    public static <L,R> Either<L,R> left(L value) {
        return new Either<>(Optional.of(value), Optional.empty());
    }
    public static <L,R> Either<L,R> right(R value) {
        return new Either<>(Optional.empty(), Optional.of(value));
    }
    private final Optional<L> left;
    private final Optional<R> right;
    private Either(Optional<L> l, Optional<R> r) {
      left=l;
      right=r;
    }
    public <T> T map(
        Function<? super L, ? extends T> lFunc,
        Function<? super R, ? extends T> rFunc)
    {
        return left.<T>map(lFunc).orElseGet(()->right.map(rFunc).get());
    }
    public <T> Either<T,R> mapLeft(Function<? super L, ? extends T> lFunc)
    {
        return new Either<>(left.map(lFunc),right);
    }
    public <T> Either<L,T> 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<String,Integer>)(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<L,R>
{
    public static <L,R> Either<L,R> left(L value) {
        return new Either<L,R>() {
            @Override public <T> T map(Function<? super L, ? extends T> lFunc,
                                       Function<? super R, ? extends T> rFunc) {
                return lFunc.apply(value);
            }
        };
    }
    public static <L,R> Either<L,R> right(R value) {
        return new Either<L,R>() {
            @Override public <T> T map(Function<? super L, ? extends T> lFunc,
                                       Function<? super R, ? extends T> rFunc) {
                return rFunc.apply(value);
            }

        };
    }
    private Either() {}
    public abstract <T> T map(
      Function<? super L, ? extends T> lFunc, Function<? super R, ? extends T> rFunc);

    public <T> Either<T,R> mapLeft(Function<? super L, ? extends T> lFunc) {
        return this.<Either<T,R>>map(t -> left(lFunc.apply(t)), t -> (Either<T,R>)this);
    }
    public <T> Either<L,T> mapRight(Function<? super R, ? extends T> lFunc) {
        return this.<Either<L,T>>map(t -> (Either<L,T>)this, t -> right(lFunc.apply(t)));
    }
    public void apply(Consumer<? super L> lFunc, Consumer<? super R> rFunc) {
        map(consume(lFunc), consume(rFunc));
    }
    private <T> Function<T,Void> consume(Consumer<T> 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.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 12
    Keep in mind that while this behaves like `Either`, the type is in some sense "too big", since your `left` and `right` fields could in principle both be empty or both be defined. You've hidden the constructors that would make that possible, but the approach still leaves potential for bugs in your implementation. In simple type arithmetic terms, you're trying to get `a + b` out of `(1 + a) * (1 + b)`. Sure, `a + b` occurs in the result of that expression, but so is `1` and `a * b`. – Mysterious Dan Oct 02 '14 at 15:52
  • 9
    @Mysterious Dan: forbidding certain kind of state during object construction is the preferred way in Java. Otherwise you would have to invent a new “valid range” type for almost every use case of `int` as using the entire value range of `int` when using an `int` variable is the exception, just as an example. After all, `Optional` does the same, enforcing the invariants during object construction. – Holger Oct 06 '14 at 08:17
  • 2
    @Holger: `Either.left(42).map(left -> null, right -> right)` throws `NoSuchElementException` (correct) on `this.right.get()` (incorrect). Also, one can bypass the invariants enforcement and produce `Either` by `Either.left(42).mapLeft(left -> null)`. Or when put together, fail again on `Either.left(42).mapLeft(left -> null).map(left -> left, right -> right)`. – charlie Jun 09 '16 at 11:52
  • 2
    @charlie: this solution does not consider that `Optional.map` allows the function to return `null`, turning it to an empty `Optional`. However, besides the opportunity to detect that and throw immediately, I don’t see any alternative solution that is “more correct”. Afaik, there is no reference behavior, as in Scala, you can’t map to `null`… – Holger Jun 09 '16 at 12:05
  • 1
    @Holger: I agree there is no "more correct" way, I just didn't like the fact that the `Right` code path is executed at all for a `Left` instance, even though the error is the same. And yes, I'd prefer failing right away instead of receiving an `` and failing later on. But again, it's all just a matter of a taste / style. – charlie Jun 10 '16 at 16:17
  • 2
    @Holger You could have `class Left extends Either` and `class Right extends Either`—that's perfectly Java-style, since it's an use of the Strategy pattern. – Blaisorblade Oct 27 '16 at 11:20
  • 2
    @Blaisorblade: that would be an alternative implementation and the unavoidable code duplication would be minimal, so, yes, that would be an option. But for the users of the class, it doesn’t make a difference anyway. Note that the JRE implementors decided not to create `EmptyOptional` and `NonEmptyOptional` implementation classes, but use conditionals internally. That likely reduces the MetaSpace consumption, however, I think the technical differences are too small to call one approach superor to the other. I wrote this class ad-hoc two years ago and there surely are alternatives… – Holger Oct 27 '16 at 11:34
  • 1
    @Blaisorblade: thinking about it, using `class Left extends Either` and `class Right extends Either` without `Optional`s might make more robust code, so perhaps, that would be the better choice for production code, but anyway, the answer to the actually asked question doesn’t change, there is no such built-in solution and you have to implement it yourself or use a 3rd party solution. – Holger Oct 27 '16 at 11:36
  • @Holger StackOverflow tries to have accurate answers, and this answer still has buggy code and claims rolling your own solution is easy. I've edited the answer to warn users, and my downvote stays. – Blaisorblade Nov 02 '16 at 22:28
  • 2
    @Blaisorblade: there is no bug mentioned here, there’s only an unsupported case, that has no well-defined behavior given by the question, as only Scala’s `Either` has been specified and Scala has no `null`. Once there is a specified behavior for that case, adapting this code to that behavior *is* easy. – Holger Nov 03 '16 at 09:38
  • @Holger FWIW Scala definitely has `null`. I guess the best answer is "people should not roll their own". – Blaisorblade Nov 04 '16 at 08:41
  • 2
    @Blaisorblade: and you can have an `Either` containing a `null`? That would render the entire concept of `Option[T]` and `Either[L, R]` useless. – Holger Nov 04 '16 at 08:46
  • @Holger While I see your point, to the contrary, Java forbids `Optional.of(null)`, but that turns out to make it useless for many uses—see https://developer.atlassian.com/blog/2015/08/optional-broken/ or search "optional is not a monad". In fact, that's what introduces the problem with your library. In practice I'd want to prevent `Some(null)` or replace it by `None`, but that should be an explicit operation. Either way, function returning `null` (on purpose) abound in Java, so you don't want to use a library not supporting them. – Blaisorblade Nov 08 '16 at 22:59
  • Heads-up: in version 9 javac rejects the `map` method, because two distinct captures cannot be compatible. ecj reports the same since version 4.5 – Stephan Herrmann Apr 13 '17 at 19:57
  • 1
    @Stephan Herrmann: I have inserted the necessary ``, but I’m hoping for a future, when compilers try inferring ground types first, instead of useless wildcards… – Holger Apr 18 '17 at 18:04
  • @Holger: are there plans to improve this in JLS? If so, do you have a bug number I can follow? – Stephan Herrmann Apr 18 '17 at 21:24
  • 1
    @Stephan Herrmann: I don’t know of any actual plans, that’s why I said, I’m “hoping”. Since apparently implementing the JLS as-is does already consume all resources, I’m not too optimistic. – Holger Apr 19 '17 at 07:01
  • 1
    Sorry, my Java skill is to weak.The second `Either` class, where I do not have a field. How can I decide that a object is Left or Right? – ula.uvula Jul 30 '20 at 15:28
  • 1
    @ula.uvula the left and right cases are implemented via two anonymous subclasses. Since they don’t have names, you can’t test an instance for being the left or right class. But you can simply use the program logic, e.g. `either.map(x -> "left", x -> "right")` will return the answer. Or `static boolean isLeft(Either,?> either) { return either.map(x -> true, x -> false); }` – Holger Jul 31 '20 at 08:09
  • 1
    @Holger: thanks, it work, also as as object method. So, I changed your second approach that I can run also function with checked exceptions. So if I get an exception, I return a "Left" (containing the exception and the value, raising the exception). In combination with streams: I convert a stream first all in Either object (Rights), than I can use map/filter etc. All without dying. The terminal operation of the stream can decide, what to do with the Left object. I can now run stream, without d – ula.uvula Jul 31 '20 at 19:00
  • I've created a polished version of the first solution @Holger provided: https://stackoverflow.com/a/76435293/501113 – chaotic3quilibrium Jun 08 '23 at 22:42
29

At the time of writing, vavr (formerly javaslang) is probably the most popular functional Java 8 library. It is pretty similar to lambda-companion's Either in my other answer.

Either<String,Integer> value = compute().right().map(i -> i * 2).toEither();
Guy Bolton King
  • 973
  • 1
  • 8
  • 17
whirlwin
  • 16,044
  • 17
  • 67
  • 98
19

There is no Either in the Java Standard Library. However there is an implementation of Either in FunctionalJava, along with many other nice classes.

Ravi Kiran
  • 1,139
  • 6
  • 10
10

cyclops-react has a 'right' biased either implementation called Xor.

 Xor.primary("hello")
    .map(s->s+" world")

 //Primary["hello world"]

 Xor.secondary("hello")
    .map(s->s+" world")

 //Secondary["hello"]

 Xor.secondary("hello")
    .swap()
    .map(s->s+" world")

 //Primary["hello world"]

Xor.accumulateSecondary(ListX.of(Xor.secondary("failed1"),
                                 Xor.secondary("failed2"),
                                 Xor.primary("success")),
                                 Semigroups.stringConcat)

//failed1failed2

There is also a related type Ior which can act as an either or a tuple2.

  • disclosure I am the author of cyclops-react.
John McClean
  • 5,225
  • 1
  • 22
  • 30
  • 3
    `Xor` was [renamed](https://github.com/aol/cyclops-react/issues/749) to `Either` in Cyclops X: https://static.javadoc.io/com.oath.cyclops/cyclops/10.0.0-FINAL/cyclops/control/Either.html – seanf Aug 23 '18 at 05:56
8

No, there is none.

Java language developers explicitly state that types like Option<T> are intended to be used only as temporary values (e.g. in stream operations results), so while they are the same thing as in other languages, they are not supposed to be used as they are used in other languages. So it is not surprising that there is no such thing as Either because it does not arise naturally (e.g. from stream operations) like Optional does.

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • 9
    Do you have a source on that? – Cannoliopsida Sep 30 '15 at 20:50
  • 3
    @akroy, this seems to be correct, Brian Goetz wrote as much in this answer: [link](http://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555). – RonyHe Sep 01 '16 at 10:10
  • 4
    For me `Either` does arise naturally. Maybe I'm doing it wrong. What do you do when a method could return two different things? Like, `Either, SomeOtherClass>`? – tamas.kenez Oct 20 '18 at 13:14
  • 6
    I would object too that Either arises naturally, for me in a stream where a map operation could potentially throw an exception, so I map to a stream of Either – Olivier Gérardin May 15 '19 at 15:08
  • It arose natually for me, too. So much so, I took @Holder's first implementation with two `Optional`s and polished it to be more useful to me: https://stackoverflow.com/a/76435293/501113 – chaotic3quilibrium Jun 08 '23 at 23:26
6

There is a stand-alone implementation of Either in a small library, "ambivalence": http://github.com/poetix/ambivalence

You can get it from Maven central:

<dependency>
    <groupId>com.codepoetics</groupId>
    <artifactId>ambivalence</artifactId>
    <version>0.2</version>
</dependency>
Dominic Fox
  • 1,039
  • 11
  • 8
5

lambda-companion has an Either type (and a few other functional types e.g. Try)

<dependency>
    <groupId>no.finn.lambda</groupId>
    <artifactId>lambda-companion</artifactId>
    <version>0.25</version>
</dependency>

Using it is easy:

final String myValue = Either.right("example").fold(failure -> handleFailure(failure), Function.identity())
whirlwin
  • 16,044
  • 17
  • 67
  • 98
1

NOTE: For an enhanced and fully documented version of the Either class show below (including equals, hashCode, flatMap, and other miscellaneous helper functions), please visit this Gist.


I have taken the implementation @Holger provided in his (currently top voted) Answer, and polished it a bit to eliminate all the null issues I could find.

I refactored it to be more naming convention aligned with the OpenJDK.

I then read through the comments, and made tweaks and adjustments to further improve the quality of the implementation.

I have also added state validation to the constructor, and added a couple of helper methods, isLeft(), isRight(), getLeft(), and getRight().

/**
 * Implementation of Either<L, R> via a pair of Optionals which explicitly
 * reject null values.
 * <p>
 * Inspired by the (first) solution presented in this StackOverflow Answer:
 * <a href="https://stackoverflow.com/a/26164155/501113">...</a>
 **/
public static final class Either<L, R> {

  public static <L, R> Either<L, R> left(L value) {
    return new Either<>(Optional.of(value), Optional.empty());
  }

  public static <L, R> Either<L, R> right(R value) {
    return new Either<>(Optional.empty(), Optional.of(value));
  }

  private final Optional<L> left;
  private final Optional<R> right;

  private Either(Optional<L> left, Optional<R> right) {
    if (left.isEmpty() == right.isEmpty()) {
      throw new IllegalArgumentException(
          "left.isEmpty() and right.isEmpty() cannot be equal");
    }
    this.left = left;
    this.right = right;
  }

  public boolean isLeft() {
    return this.left.isPresent();
  }

  public boolean isRight() {
    return this.right.isPresent();
  }

  public L getLeft() {
    return this.left.get();
  }

  public R getRight() {
    return this.right.get();
  }

  public <T> T map(
      Function<? super L, ? extends T> leftFunction,
      Function<? super R, ? extends T> rightFunction
  ) {
    return this.left
        .<T>map(l -> Objects.requireNonNull(leftFunction.apply(l)))
        .orElseGet(() ->
            this.right
                .map(r -> Objects.requireNonNull(rightFunction.apply(r)))
                .orElseThrow(() ->
                    new IllegalStateException(
                        "should never get here")));
  }

  public <T> Either<T, R> mapLeft(
    Function<? super L, ? extends T> leftFunction
  ) {
    return new Either<>(
        this.left.map(l ->
            Objects.requireNonNull(leftFunction.apply(l))),
        this.right);
  }

  public <T> Either<L, T> mapRight(
    Function<? super R, ? extends T> rightFunction
  ) {
    return new Either<>(
        this.left,
        this.right.map(r -> 
            Objects.requireNonNull(rightFunction.apply(r))));
  }

  public void forEach(
    Consumer<? super L> leftAction,
    Consumer<? super R> rightAction
  ) {
    this.left.ifPresent(leftAction);
    this.right.ifPresent(rightAction);
  }
}

NOTE: For an enhanced and fully documented version of the Either class show below (including equals, hashCode, flatMap, and other miscellaneous helper functions), please visit this Gist.

chaotic3quilibrium
  • 5,661
  • 8
  • 53
  • 86