web-dev-qa-db-fra.com

Existe-t-il un équivalent de Scala soit dans Java 8?

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]?

66
HRJ

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.

57
Holger

Voir Fugue d'Atlassian . Il y a une bonne implémentation de Either ici.

22
ZhekaKozlov

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();
20
whirlwin

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.

17
Ravi Kiran

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.

  • divulgation Je suis l'auteur de cyclops-react.
9
John McClean

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())
5
whirlwin

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>
4
Dominic Fox

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.

4
Vladimir Matveev