Interface Either<L,R>

Type Parameters:
L - the type of the left value
R - the type of the right value
All Known Implementing Classes:
Either.Left, Either.Right

@NullMarked public sealed interface Either<L,R> permits Either.Left<L,R>, Either.Right<L,R>
A disjoint union that is either a Either.Left or a Either.Right.

Either<L,R> is a neutral two-track type: neither side carries error semantics. Use it when a computation legitimately returns one of two value types without implying that one side is a failure. For error-handling use cases prefer Result, which is semantically opinionated (Ok = success, Err = failure) and offers richer recovery operations.

By convention, map, flatMap, and related operations act on the right side. Use swap() to flip the sides when you need to operate on the left.

This semantic neutrality — and the decision to keep right-biased operations without error/success connotation — is documented in ADR-007 — Either as a neutral type with no directional bias.

This interface is @NullMarked: all values — left and right — are non-null by default.

  • Method Details

    • left

      static <L,R> Either<L,R> left(L value)
      Creates a Either.Left instance wrapping the given value.
      Type Parameters:
      L - the left type
      R - the right type
      Parameters:
      value - the non-null left value
      Returns:
      an Either containing the left value
      Throws:
      NullPointerException - if value is null
    • right

      static <L,R> Either<L,R> right(R value)
      Creates a Either.Right instance wrapping the given value.
      Type Parameters:
      L - the left type
      R - the right type
      Parameters:
      value - the non-null right value
      Returns:
      an Either containing the right value
      Throws:
      NullPointerException - if value is null
    • isLeft

      default boolean isLeft()
      Returns true if this is a Either.Left.
      Returns:
      true for Left, false for Right
    • isRight

      default boolean isRight()
      Returns true if this is a Either.Right.
      Returns:
      true for Right, false for Left
    • getLeft

      default L getLeft()
      Returns the left value.
      Returns:
      the left value
      Throws:
      NoSuchElementException - if this is a Either.Right
    • getRight

      default R getRight()
      Returns the right value.
      Returns:
      the right value
      Throws:
      NoSuchElementException - if this is a Either.Left
    • getRightOrElse

      default R getRightOrElse(R fallback)
      Returns the right value if present, or fallback if this is a Either.Left.
      Parameters:
      fallback - the non-null value returned when this is Left
      Returns:
      the right value, or fallback
      Throws:
      NullPointerException - if fallback is null
    • getRightOrElseGet

      default R getRightOrElseGet(Supplier<? extends R> supplier)
      Returns the right value if present, or the value supplied by supplier if this is a Either.Left. The supplier is called lazily — only when this is Left.
      Parameters:
      supplier - provides the fallback value; must not be null and must not return null
      Returns:
      the right value, or the result of supplier
      Throws:
      NullPointerException - if supplier is null or returns null
    • getLeftOrElse

      default L getLeftOrElse(L fallback)
      Returns the left value if present, or fallback if this is a Either.Right.
      Parameters:
      fallback - the non-null value returned when this is Right
      Returns:
      the left value, or fallback
      Throws:
      NullPointerException - if fallback is null
    • getLeftOrElseGet

      default L getLeftOrElseGet(Supplier<? extends L> supplier)
      Returns the left value if present, or the value supplied by supplier if this is a Either.Right. The supplier is called lazily — only when this is Right.
      Parameters:
      supplier - provides the fallback value; must not be null and must not return null
      Returns:
      the left value, or the result of supplier
      Throws:
      NullPointerException - if supplier is null or returns null
    • fold

      default <T> T fold(Function<? super L, ? extends T> onLeft, Function<? super R, ? extends T> onRight)
      Applies onLeft if this is Either.Left, or onRight if this is Either.Right, and returns the result.
      Type Parameters:
      T - the result type
      Parameters:
      onLeft - function applied to the left value
      onRight - function applied to the right value
      Returns:
      the result of whichever function was applied
    • map

      default <R2> Either<L,R2> map(Function<? super R, ? extends R2> mapper)
      Maps the right value using mapper, leaving a Either.Left unchanged.
      Type Parameters:
      R2 - the new right type
      Parameters:
      mapper - function applied to the right value; must not be null and must not return null
      Returns:
      a new Either with the mapped right value, or the original Left
      Throws:
      NullPointerException - if mapper is null or returns null
    • mapLeft

      default <L2> Either<L2,R> mapLeft(Function<? super L, ? extends L2> mapper)
      Maps the left value using mapper, leaving a Either.Right unchanged.
      Type Parameters:
      L2 - the new left type
      Parameters:
      mapper - function applied to the left value; must not be null and must not return null
      Returns:
      a new Either with the mapped left value, or the original Right
      Throws:
      NullPointerException - if mapper is null or returns null
    • flatMap

      default <R2> Either<L,R2> flatMap(Function<? super R, ? extends Either<? extends L, ? extends R2>> mapper)
      Applies mapper to the right value and returns the resulting Either, leaving a Either.Left unchanged. This is the monadic bind for the right track.
      Type Parameters:
      R2 - the new right type
      Parameters:
      mapper - function that returns an Either for the right value
      Returns:
      the result of mapper applied to the right value, or the original Left
    • flatMapLeft

      default <L2> Either<L2,R> flatMapLeft(Function<? super L, ? extends Either<? extends L2, ? extends R>> mapper)
      Applies mapper to the left value and returns the resulting Either, leaving a Either.Right unchanged. This is the monadic bind for the left track, symmetric with flatMap(Function).
      Type Parameters:
      L2 - the new left type
      Parameters:
      mapper - function that returns an Either for the left value; must not be null and must not return null
      Returns:
      the result of mapper applied to the left value, or the original Right
      Throws:
      NullPointerException - if mapper is null or returns null
    • swap

      default Either<R,L> swap()
      Swaps the two sides: Either.Left becomes Either.Right and vice versa.
      Returns:
      an Either<R,L> with the sides swapped
    • peek

      default Either<L,R> peek(Consumer<? super R> action)
      Executes action if this is a Either.Right, then returns this unchanged.
      Parameters:
      action - consumer applied to the right value
      Returns:
      this Either
    • peekLeft

      default Either<L,R> peekLeft(Consumer<? super L> action)
      Executes action if this is a Either.Left, then returns this unchanged.
      Parameters:
      action - consumer applied to the left value
      Returns:
      this Either
    • toOption

      default Option<R> toOption()
      Converts this Either to an Option: Either.Right becomes Option.some(Object), Either.Left becomes Option.none().
      Returns:
      an Option containing the right value, or empty
    • toResult

      default Result<R,L> toResult()
      Converts this Either to a Result.

      Either.Right maps to Result.Ok; Either.Left maps to Result.Err. This is the inverse of Result.toEither().

      Returns:
      a Result<R, L> equivalent of this Either
    • toValidated

      default Validated<L,R> toValidated()
      Converts this Either to a Validated.

      Either.Right maps to Validated.valid(A); Either.Left maps to Validated.invalid(E). Useful for bringing a neutral Either into the error-accumulating validation world.

      Returns:
      a Validated<L, R> equivalent of this Either
    • toOptional

      default Optional<R> toOptional()
      Converts this Either to a standard Optional<R>.

      Right(r) maps to Optional.of(r); Either.Left maps to Optional.empty(), discarding the left value.

      Example:

      Either.<String, Integer>right(42).toOptional(); // Optional.of(42)
      Either.<String, Integer>left("err").toOptional(); // Optional.empty()
      
      Returns:
      an Optional containing the right value, or empty if this is Left
    • stream

      default Stream<R> stream()
      Returns this Either as a single-element or empty Stream.

      Right(r) produces Stream.of(r); Either.Left produces Stream.empty(). Useful for integrating into stream pipelines without an explicit filter.

      Example:

      Stream<Either<String, Integer>> eithers = ...;
      List<Integer> rights = eithers
          .flatMap(Either::stream)
          .toList();
      
      Returns:
      a one-element stream of the right value, or an empty stream
    • streamLeft

      default Stream<L> streamLeft()
      Returns this Either as a single-element or empty Stream of the left value.

      Left(l) produces Stream.of(l); Either.Right produces Stream.empty(). Symmetric with stream(), which operates on the right track.

      Example:

      Stream<Either<String, Integer>> eithers = ...;
      List<String> lefts = eithers
          .flatMap(Either::streamLeft)
          .toList();
      
      Returns:
      a one-element stream of the left value, or an empty stream
    • toTry

      default Try<R> toTry(Function<? super L, ? extends Throwable> leftMapper)
      Converts this Either to a Try.

      Right(r) maps to Try.success(r); Left(l) maps to Try.failure(leftMapper(l)).

      Example:

      Either.<String, Integer>right(1).toTry(IllegalStateException::new); // Try.success(1)
      Either.<String, Integer>left("bad").toTry(IllegalStateException::new); // Try.failure(...)
      
      Parameters:
      leftMapper - function that converts the left value into a Throwable; must not be null and must not return null
      Returns:
      a Try<R> equivalent of this Either
      Throws:
      NullPointerException - if leftMapper is null or returns null
    • match

      default void match(Consumer<? super L> onLeft, Consumer<? super R> onRight)
      Executes one of the provided consumers depending on whether this is a Either.Left or a Either.Right, then discards the result. This is the terminal, side-effecting counterpart to fold(Function, Function).

      Example:

      either.match(
          left  -> log.warn("Left: {}", left),
          right -> process(right)
      );
      
      Parameters:
      onLeft - consumer executed when this is Left; must not be null
      onRight - consumer executed when this is Right; must not be null
      Throws:
      NullPointerException - if onLeft or onRight is null