webentwicklung-frage-antwort-db.com.de

Gibt es ein Äquivalent zu Scala's Either in Java 8?

So wie Java.util.Optional<T> in Java 8 ist (etwas) äquivalent zu Scala's Option[T] Typ, gibt es ein Äquivalent zu Scala Either[L, R]?

66
HRJ

Es gibt keinen Either -Typ für Java 8, daher müssen Sie selbst einen erstellen oder eine Bibliothek eines Drittanbieters verwenden.

Sie können eine solche Funktion mit dem neuen Typ Optional erstellen (aber bis zum Ende dieser Antwort lesen):

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);
    }
}

Anwendungsbeispiel:

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));}
));

Rückblickend ist die auf Optional basierende Lösung eher ein akademisches Beispiel, aber kein empfohlener Ansatz. Ein Problem ist die Behandlung von null als "leer", was der Bedeutung von "entweder" widerspricht.

Der folgende Code zeigt einen Either, der null als möglichen Wert betrachtet. Er ist also entweder links oder rechts, auch wenn der Wert null ist:

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; };
    }
}

Es ist einfach, dies in eine strikte Ablehnung von null umzuwandeln, indem Sie einfach eine Objects.requireNonNull(value) am Anfang beider Factory-Methoden einfügen. Ebenso wäre es vorstellbar, Unterstützung für ein leeres Element hinzuzufügen.

57
Holger

Siehe Atlassische Fuge . Dort gibt es eine gute Implementierung von Either.

22
ZhekaKozlov

Zum Zeitpunkt des Schreibens war vavr (früher javaslang) wahrscheinlich die beliebteste funktionale Java 8 Bibliothek. Sie ähnelt der von lambda-companion in meiner anderen Antwort ziemlich .

Either<String,Integer> value = compute().right().map(i -> i * 2).toEither();
20
whirlwin

In der Standardbibliothek Java) gibt es keine. Es gibt jedoch eine Implementierung von Either in FunctionalJava , zusammen mit vielen anderen Nice-Klassen.

17
Ravi Kiran

cyclops-react hat eine 'richtige' verzerrte Implementierung namens 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

Es gibt auch einen verwandten Typ Ior , der entweder als Tuple2 oder als Tuple2 fungieren kann.

  • offenlegung Ich bin der Autor von Zyklop-Reaktion.
9
John McClean

Lambda-Begleiter hat einen Either -Typ (und einige andere funktionale Typen, z. B. Try)

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

Die Benutzung ist einfach:

final String myValue = Either.right("example").fold(failure -> handleFailure(failure), Function.identity())
5
whirlwin

Es gibt eine eigenständige Implementierung von Either in einer kleinen Bibliothek, "ambivalence": http://github.com/poetix/ambivalence

Sie können es von Maven Central bekommen:

<dependency>
    <groupId>com.codepoetics</groupId>
    <artifactId>ambivalence</artifactId>
    <version>0.2</version>
</dependency>
4
Dominic Fox

Nein, es gibt keine.

Java-Sprachentwickler geben ausdrücklich an, dass Typen wie Option<T> sollen nur als temporäre Werte verwendet werden (z. B. in Stream-Operations-Ergebnissen). Während sie sind dasselbe wie in anderen Sprachen, sind sie nicht angenommen zu verwendet werden, wie sie in anderen Sprachen verwendet werden. Es ist also nicht verwunderlich, dass es so etwas wie Either nicht gibt, da es nicht auf natürliche Weise entsteht (z. B. durch Stream-Operationen) wie Optional.

4