在Java 8中是否有与Scala的Either等效的东西?

1u4esq0p  于 2023-06-23  发布在  Scala
关注(0)|答案(8)|浏览(164)

就像Java 8中的java.util.Optional<T>(在某种程度上)等价于Scala的Option[T]类型一样,是否有一个等价于Scala的Either[L, R]的类型?

mtb9vblg

mtb9vblg1#

Java 8中没有Either类型,因此您需要自己创建一个或使用一些第三方库。
您可以使用新的Optional类型构建这样的功能(但请阅读本答案的结尾):

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

示例用例:

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

回顾过去,基于Optional的解决方案更像是一个学术示例,但不是推荐的方法。一个问题是将null视为“空”,这与“任一”的含义相矛盾。
下面的代码显示了一个Either,它认为null是一个可能的值,所以它是严格的“要么”,左或右,即使值是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; };
    }
}

很容易将其更改为严格拒绝null,只需在两个工厂方法的开头插入Objects.requireNonNull(value)。同样,添加对空either的支持也是可以想象的。

xam8gpfp

xam8gpfp2#

在撰写本文时,vavr(以前的javaslang)可能是最流行的Java 8函数库。这是非常相似的lambda-companion的要么在我的另一个答案。

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

jecbmhm33#

Java标准库中没有Either。然而,在FunctionalJava中有Either的实现,沿着许多其他不错的类。

ie3xauqp

ie3xauqp4#

cyclops-react有一个“右”偏置的实现,称为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

还有一个相关的类型Ior,它可以充当either或tuple 2。

  • 我是cyclops-react的作者。
a2mppw5e

a2mppw5e5#

不,没有。
Java语言开发人员明确指出,像Option<T>这样的类型只打算用作临时值(例如在流操作结果中),所以虽然它们与其他语言中的东西是一样的,但它们不应该像其他语言中使用的那样被使用。因此,不存在Either这种东西并不奇怪,因为它不是自然产生的(例如从流操作),如Optional所做的。

cotxawn7

cotxawn76#

在一个小库中有一个Either的独立实现,“ambivalence”:http://github.com/poetix/ambivalence
您可以从Maven Central获得:

<dependency>
    <groupId>com.codepoetics</groupId>
    <artifactId>ambivalence</artifactId>
    <version>0.2</version>
</dependency>
htzpubme

htzpubme7#

lambda-companion有一个Either类型(以及一些其他功能类型,例如Try

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

使用它很容易:

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

kyks70gy8#

**注意:**有关以下Either类的增强和完整文档版本(包括equalshashCodeflatMap和其他杂项帮助函数),请访问this Gist

我采用了@Holger在他的(目前投票最高的)Answer中提供的实现,并对其进行了一些改进,以消除我能找到的所有null问题。
我将它重构为与OpenJKD更加一致的命名约定。
然后我通读了评论,并进行了调整和调整,以进一步提高实施的质量。
我还在构造函数中添加了状态验证,并添加了两个辅助方法:isLeft()isRight()getLeft()getRight()

/**
 * Implementation of Either<L, R> via a pair of Optionals which explicitly
 * reject null values.
 * <p>
 * Inspired by the (first) solution presented in this StackOverflow Answer:
 * <a href="https://stackoverflow.com/a/26164155/501113">...</a>
 **/
public static 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> left, Optional<R> right) {
    if (left.isEmpty() == right.isEmpty()) {
      throw new IllegalArgumentException(
          "left.isEmpty() and right.isEmpty() cannot be equal");
    }
    this.left = left;
    this.right = right;
  }

  public boolean isLeft() {
    return this.left.isPresent();
  }

  public boolean isRight() {
    return this.right.isPresent();
  }

  public L getLeft() {
    return this.left.get();
  }

  public R getRight() {
    return this.right.get();
  }

  public <T> T map(
      Function<? super L, ? extends T> leftFunction,
      Function<? super R, ? extends T> rightFunction
  ) {
    return this.left
        .<T>map(l -> Objects.requireNonNull(leftFunction.apply(l)))
        .orElseGet(() ->
            this.right
                .map(r -> Objects.requireNonNull(rightFunction.apply(r)))
                .orElseThrow(() ->
                    new IllegalStateException(
                        "should never get here")));
  }

  public <T> Either<T, R> mapLeft(
    Function<? super L, ? extends T> leftFunction
  ) {
    return new Either<>(
        this.left.map(l ->
            Objects.requireNonNull(leftFunction.apply(l))),
        this.right);
  }

  public <T> Either<L, T> mapRight(
    Function<? super R, ? extends T> rightFunction
  ) {
    return new Either<>(
        this.left,
        this.right.map(r -> 
            Objects.requireNonNull(rightFunction.apply(r))));
  }

  public void forEach(
    Consumer<? super L> leftAction,
    Consumer<? super R> rightAction
  ) {
    this.left.ifPresent(leftAction);
    this.right.ifPresent(rightAction);
  }
}

**注意:**有关以下Either类的增强和完整文档版本(包括equalshashCodeflatMap和其他杂项帮助函数),请访问this Gist

相关问题