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]
?
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.
Siehe Atlassische Fuge . Dort gibt es eine gute Implementierung von Either
.
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();
In der Standardbibliothek Java) gibt es keine. Es gibt jedoch eine Implementierung von Either in FunctionalJava , zusammen mit vielen anderen Nice-Klassen.
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.
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())
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>
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
.