Juste comme Java.util.Optional<T>
in Java 8 est (en quelque sorte) équivalent à Scala's Option[T]
type, existe-t-il un équivalent du Either[L, R]
?
Il n'y a pas de type Either
est Java 8, vous devez donc en créer un vous-même ou utiliser une bibliothèque tierce.
Vous pouvez construire une telle fonctionnalité en utilisant le nouveau type Optional
(mais lisez jusqu'à la fin de cette réponse):
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);
}
}
Exemple d'utilisation:
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));}
));
En rétrospective, la solution basée sur Optional
ressemble davantage à un exemple théorique, mais ne constitue pas une approche recommandée. Un problème est le traitement de null
comme "vide", ce qui contredit le sens du mot "soit".
Le code suivant montre un Either
qui considère null
comme une valeur possible. Il s’agit donc strictement de "soit", à gauche ou à droite, même si la valeur est 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; };
}
}
Il est facile de changer cela en un rejet strict de null
en insérant simplement une Objects.requireNonNull(value)
au début des deux méthodes d'usine. De même, ajouter un support pour un vide serait imaginable.
Voir Fugue d'Atlassian . Il y a une bonne implémentation de Either
ici.
Au moment de la rédaction de ce document, vavr (anciennement javaslang) est probablement la bibliothèque fonctionnelle la plus populaire Java 8. Elle est assez similaire à celle de lambda-companion, soit dans mon autre réponse .
Either<String,Integer> value = compute().right().map(i -> i * 2).toEither();
Il n'y a pas non plus dans Java Bibliothèque standard. Cependant, il existe une implémentation de Soit dans FunctionalJava , ainsi que de nombreuses autres classes de Nice.
cyclops-react a une implémentation polarisée vers la droite appelée 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
Il existe également un type associé Ior qui peut jouer le rôle d'un ou d'un Tuple2.
lambda-companion a un type Either
(et quelques autres types fonctionnels, par exemple Try
)
<dependency>
<groupId>no.finn.lambda</groupId>
<artifactId>lambda-companion</artifactId>
<version>0.25</version>
</dependency>
Utiliser c'est facile:
final String myValue = Either.right("example").fold(failure -> handleFailure(failure), Function.identity())
Il y a une implémentation autonome de Either
dans une petite bibliothèque, "ambivalence": http://github.com/poetix/ambivalence
Vous pouvez l'obtenir auprès de Maven central:
<dependency>
<groupId>com.codepoetics</groupId>
<artifactId>ambivalence</artifactId>
<version>0.2</version>
</dependency>
Non, il n'y en a pas.
Les développeurs de langage Java indiquent explicitement que des types tels que Option<T>
sont destinés à n'être utilisés que comme valeurs temporaires (par exemple, dans les résultats d'opérations de flux). Ainsi, bien qu'ils soient identiques à ceux des autres langues, ils sont non supposés être utilisés comme ils sont utilisés dans d'autres langues. Il n’est donc pas surprenant que Either
n’existe pas car il n’apparaît pas naturellement (par exemple, à partir d’opérations de flux) comme Optional
.