Java 8에 Scala의 Either와 동등한 것이 있습니까?


81

java.util.Optional<T>Java 8에서 (다소) Scala의 Option[T]유형 과 동등한 것처럼 Scala의 유형과 동등한 것이 Either[L, R]있습니까?


이 모든 시간 후에도 여전히 표준 구현이 없습니다. (
Cherry

항상 java.lang.Object ... 슬프다.
Alex R

답변:


68

EitherJava 8은 유형 이 없으므로 직접 작성하거나 타사 라이브러리를 사용해야합니다.

새로운 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)있습니다. 마찬가지로, 빈 둘 중 하나에 대한 지원을 추가하는 것은 상상할 수 있습니다.


11
이것이와 같이 작동하는 동안 Either유형은 어떤 의미에서는 "너무 큽니다"라는 점을 명심하십시오. 원칙적으로 leftright필드는 모두 비어 있거나 둘 다 정의 될 수 있기 때문입니다. 이를 가능하게하는 생성자를 숨겼지만 접근 방식은 여전히 ​​구현에 버그가 발생할 가능성이 있습니다. 간단한 유형 산술 용어 a + b(1 + a) * (1 + b). 물론, a + b해당 표현식의 결과에 발생하지만 1a * b.
Mysterious Dan

6
@Mysterious Dan : 객체 생성 중에 특정 종류의 상태를 금지하는 것이 Java에서 선호되는 방법입니다. 그렇지 않으면 예와 int같이 변수를 int사용하는 int것이 예외 일 때 의 전체 값 범위를 사용하는 거의 모든 사용 사례에 대해 새로운 "유효 범위"유형을 발명해야합니다 . 결국 Optional동일한 작업을 수행하여 객체 생성 중에 불변성을 적용합니다.
홀거

1
@Holger : (올바르지 않음) 에 (올바른) Either.left(42).map(left -> null, right -> right)던집니다 . 또한, 사람은 불변의 집행 및 생산 무시할 수 에 의해를 . 또는 합치면에서 다시 실패하십시오 . NoSuchElementExceptionthis.right.get()Either<empty, empty>Either.left(42).mapLeft(left -> null)Either.left(42).mapLeft(left -> null).map(left -> left, right -> right)
찰리

2
@charlie :이 솔루션은 Optional.map함수가 반환 되도록 허용 하지 않고 null비어있는 Optional. 그러나이를 감지하고 즉시 던질 수있는 기회 외에는 "더 정확한"대체 솔루션이 보이지 않습니다. AFAIK는 스칼라에서, 당신은 매핑 할 수 없습니다, 더 참조 동작이 없습니다에 null...
홀거

1
@Holger : "더 정확한"방법은 없다는 데 동의 합니다. 오류가 동일하더라도 Right코드 경로가 Left인스턴스에 대해 전혀 실행 된다는 사실이 마음에 들지 않았습니다 . 그리고 예, 나는 <empty, empty>나중에 실패 하는 대신 즉시 실패하는 것을 선호 합니다. 그러나 다시 말하지만, 그것은 모두 단지 취향 / 스타일의 문제입니다.
찰리

26

글을 쓰는 시점에서 vavr (이전의 javaslang)은 아마도 가장 인기있는 기능적 Java 8 라이브러리 일 것입니다. 내 다른 대답에서 lambda-companion의 Either와 매우 유사합니다.

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


17

Java Standard Library에는 Either가 없습니다. 그러나의 구현이 하나FunctionalJava 다른 많은 좋은 클래스와 함께이.


Either에 대한 링크가 삭제 된 것 같습니다. 프로젝트가 여전히 지원되고 있습니까?
Flame_Phoenix

링크를 업데이트했습니다. 이 프로젝트는 여전히 AFAIK를 적극적으로 유지 관리하고 있습니다.
Ravi Kiran

하지만 문서는 형편 없다. :(
Misha Tavkhelidze

10

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

둘 중 하나 또는 튜플 2 역할을 할 수 있는 관련 유형 Ior 도 있습니다.

  • 공개 저는 cyclops-react의 저자입니다.


6

아니요, 없습니다.

종류가 좋아하는 것이 자바 개발자가 명시 적으로 상태 Option<T>임시 값으로 사용하기위한 것입니다 (예를 들어 스트림 작업 결과에), 그들이 그렇게하는 동안 있습니다 다른 언어로 같은 것을, 그들이하는 그들은 다른에 사용되는로 사용하기 언어. 따라서 Either자연적으로 발생하지 않기 때문에 (예 : 스트림 작업에서) 발생하는 것과 같은 것이 없다는 것은 놀라운 일 이 아닙니다 Optional.


8
그것에 대한 출처가 있습니까?
Cannoliopsida 2015 년

3
@akroy이 올바른 것 같다, 브라이언 게츠는이 답변에 많이 썼다 : 링크를 .
RonyHe

2
나를 위해 Either자연스럽게 발생합니다. 어쩌면 내가 잘못하고있는 것 같습니다. 메서드가 서로 다른 두 가지를 반환 할 수있을 때 어떻게합니까? 처럼 Either<List<String>, SomeOtherClass>?
tamas.kenez

3
나도 <예외, 결과>의 스트림에 매핑 그래서 나는지도 작업이 잠재적으로 예외를 던질 수있는 스트림에서 어느 날 위해, 자연적으로 발생하는 것이 너무 객체 것이다
올리비에 Gérardin

6

Either작은 라이브러리에 "ambivalence"라는 독립 실행 형 구현이 있습니다 : http://github.com/poetix/ambivalence

Maven 중앙에서 가져올 수 있습니다.

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

5

람다 동반자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())

1
프로젝트는 더 이상 유지 보수 중이 아닙니다.
Flame_Phoenix
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.