java.util.Optional<T>
Java 8에서 (다소) Scala의 Option[T]
유형 과 동등한 것처럼 Scala의 유형과 동등한 것이 Either[L, R]
있습니까?
답변:
Either
Java 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)
있습니다. 마찬가지로, 빈 둘 중 하나에 대한 지원을 추가하는 것은 상상할 수 있습니다.
Either
유형은 어떤 의미에서는 "너무 큽니다"라는 점을 명심하십시오. 원칙적으로 left
및 right
필드는 모두 비어 있거나 둘 다 정의 될 수 있기 때문입니다. 이를 가능하게하는 생성자를 숨겼지만 접근 방식은 여전히 구현에 버그가 발생할 가능성이 있습니다. 간단한 유형 산술 용어 a + b
로 (1 + a) * (1 + b)
. 물론, a + b
해당 표현식의 결과에 발생하지만 1
및 a * b
.
int
같이 변수를 int
사용하는 int
것이 예외 일 때 의 전체 값 범위를 사용하는 거의 모든 사용 사례에 대해 새로운 "유효 범위"유형을 발명해야합니다 . 결국 Optional
동일한 작업을 수행하여 객체 생성 중에 불변성을 적용합니다.
Either.left(42).map(left -> null, right -> right)
던집니다 . 또한, 사람은 불변의 집행 및 생산 무시할 수 에 의해를 . 또는 합치면에서 다시 실패하십시오 . NoSuchElementException
this.right.get()
Either<empty, empty>
Either.left(42).mapLeft(left -> null)
Either.left(42).mapLeft(left -> null).map(left -> left, right -> right)
Optional.map
함수가 반환 되도록 허용 하지 않고 null
비어있는 Optional
. 그러나이를 감지하고 즉시 던질 수있는 기회 외에는 "더 정확한"대체 솔루션이 보이지 않습니다. AFAIK는 스칼라에서, 당신은 매핑 할 수 없습니다, 더 참조 동작이 없습니다에 null
...
Right
코드 경로가 Left
인스턴스에 대해 전혀 실행 된다는 사실이 마음에 들지 않았습니다 . 그리고 예, 나는 <empty, empty>
나중에 실패 하는 대신 즉시 실패하는 것을 선호 합니다. 그러나 다시 말하지만, 그것은 모두 단지 취향 / 스타일의 문제입니다.
Atlassian Fugue를 참조하십시오 . 거기에 좋은 구현이 Either
있습니다.
Java Standard Library에는 Either가 없습니다. 그러나의 구현이 하나 의 FunctionalJava 다른 많은 좋은 클래스와 함께이.
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 도 있습니다.
Xor
Cyclops X에서 이름 이 변경 되었습니다 Either
. static.javadoc.io/com.oath.cyclops/cyclops/10.0.0-FINAL/cyclops/…
아니요, 없습니다.
종류가 좋아하는 것이 자바 개발자가 명시 적으로 상태 Option<T>
임시 값으로 사용하기위한 것입니다 (예를 들어 스트림 작업 결과에), 그들이 그렇게하는 동안 있습니다 다른 언어로 같은 것을, 그들이하는 안 그들은 다른에 사용되는로 사용하기 언어. 따라서 Either
자연적으로 발생하지 않기 때문에 (예 : 스트림 작업에서) 발생하는 것과 같은 것이 없다는 것은 놀라운 일 이 아닙니다 Optional
.
Either
자연스럽게 발생합니다. 어쩌면 내가 잘못하고있는 것 같습니다. 메서드가 서로 다른 두 가지를 반환 할 수있을 때 어떻게합니까? 처럼 Either<List<String>, SomeOtherClass>
?
Either
작은 라이브러리에 "ambivalence"라는 독립 실행 형 구현이 있습니다 : http://github.com/poetix/ambivalence
Maven 중앙에서 가져올 수 있습니다.
<dependency>
<groupId>com.codepoetics</groupId>
<artifactId>ambivalence</artifactId>
<version>0.2</version>
</dependency>
람다 동반자 이 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())