Java에서 선택 사항 또는 기타 선택 사항


137

Java 8 의 새로운 Optional 유형 으로 작업하고 있으며 기능적으로 지원되지 않는 일반적인 작업 인 "orElseOptional"과 같은 기능을 살펴 보았습니다.

다음 패턴을 고려하십시오.

Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
    Optional<Result> resultFromServiceB = serviceB(args);
    if (resultFromServiceB.isPresent) return resultFromServiceB;
    else return serviceC(args);
}

이 패턴에는 여러 형태가 있지만, 현재 옵션이 존재하지 않는 경우에만 호출되는 새로운 옵션을 생성하는 함수를 취하는 옵션에 대해 "orElse"를 원합니다.

구현은 다음과 같습니다.

public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
    return value != null ? this : other.get();
}

의도하지 않은 방식으로 Optional을 사용하는 경우와 같은 방법이 존재하지 않는 이유가 있는지, 사람들 이이 사건을 처리하기 위해 다른 방법을 생각해 낸 경우 궁금합니다.

내 코드로 작업하는 사람들이 반드시 존재한다는 것을 알지 못하기 때문에 사용자 정의 유틸리티 클래스 / 메소드와 관련된 솔루션은 우아하지 않다고 생각해야합니다.

또한 누구나 아는 경우 그러한 방법이 JDK 9에 포함됩니까? 어떻게 그러한 방법을 제안 할 수 있습니까? 이것은 나에게 API에 대한 눈부신 누락처럼 보입니다.


13
이 문제를 참조하십시오 . 명확히하기 위해 : 이것은 이미 Java 9에있을 예정입니다. 향후 Java 8 업데이트가
아니라면

그것은! 고마워, 내 검색에서 찾지 못했습니다.
Yona Appletree 2015 년

2
@Obicere이 문제는 대체 결과가 아니라 빈 선택에 대한 동작 이므로 적용되지 않습니다 . 옵션은 이미 OP에 필요한 것을 가지고 있지만 멋진 계단식 구문을 생성하지는 않습니다. orElseGet()
Marko Topolnik


답변:


86

이 형태의 JDK (9)의 일부입니다 or걸립니다 Supplier<Optional<T>>. 그러면 귀하의 예는 다음과 같습니다.

return serviceA(args)
    .or(() -> serviceB(args))
    .or(() -> serviceC(args));

자세한 내용은 내가 작성한 Javadoc 또는 이 게시물을 참조하십시오 .


좋은. 그 추가는 1 년이어야하며 나는 알지 못했습니다. 바이트 코드 호출 명령이 리턴 유형을 포함한 전체 서명을 참조하므로 리턴 유형을 변경할 기회가 없으므로 블로그의 질문에 대해 리턴 유형을 변경하면 이진 호환성이 손상 ifPresent됩니다. 어쨌든, 나는 그 이름 ifPresent이 어쨌든 좋지 않다고 생각 합니다. 이름에 "else"가없는 다른 모든 방법의 경우 (예 map: filter,, flatMap), 값이 없으면 아무 것도하지 않는 것이 암시됩니다 ifPresent.
Holger

가산 그래서 Optional<T> perform(Consumer<T> c)방법 것은 체인 수 있도록 perform(x).orElseDo(y)( orElseDo당신의 제안에 대한 대안으로 ifEmpty,이에 일관성이 else없는 값에 대해 뭔가를 할 수있는 모든 방법의 이름으로). 당신은 그것을 모방 할 수 perform통해 자바 9 stream().peek(x).findFirst()즉 API의 남용 비록하고 실행하는 방법은 아직 없다 Runnable를 지정하지 않고 Consumer동시에는 ...
홀거

65

현재 API를 고려한 가장 깨끗한 "시도 서비스"접근 방식은 다음과 같습니다.

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
    ()->serviceA(args), 
    ()->serviceB(args), 
    ()->serviceC(args), 
    ()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();

중요한 측면은 한 번 작성해야하는 (일정한) 작업 체인이 아니라 다른 서비스를 추가하는 것이 얼마나 쉬운 지 (또는 서비스 목록을 수정하는 것이 일반적 임)입니다. 여기에서 싱글을 추가하거나 제거하는 ()->serviceX(args)것으로 충분합니다.

스트림의 지연 평가로 인해 선행 서비스가 비어 있지 않은 것을 리턴하면 서비스가 호출되지 않습니다 Optional.


13
방금 프로젝트에서 사용 했으므로 코드 검토를 수행하지 않습니다.
Ilya Smagin

3
"orElseGet"체인보다 훨씬 깨끗하지만 읽기가 훨씬 어렵습니다.
slartidan

4
이것은 확실히 맞습니다 ... 그러나 솔직히 말해서 구문 분석하는 데 잠시 시간이 걸리며 그것이 정확하다는 확신을 가지지 않습니다. 이 예제가 있다는 것을 알고 있지만, 조금씩 평가되지 않거나 한 눈에 구별 할 수없는 다른 오류가있는 작은 변화를 상상할 수 있습니다. 나에게 이것은 유틸리티 기능 범주에 속합니다.
Yona Appletree

3
나는 스위칭 궁금 .map(Optional::get)와 함께하는 .findFirst()"읽기"로 쉽게 만들 것입니다 예를 들어 .filter(Optional::isPresent).findFirst().map(Optional::get)"옵션 :: isPresent에 해당하는 스트림의 첫 번째 요소를 찾은 다음 선택 사항 :: 얻을 적용하여 평평하게"와 같은 "읽기"할 수 있습니까?
schatten

3
웃긴, 몇 달 전에 비슷한 질문에 대해 비슷한 솔루션 을 게시했습니다 . 내가 이것을 처음 접했을 때입니다.
shmosel

34

예쁘지는 않지만 작동합니다.

return serviceA(args)
  .map(Optional::of).orElseGet(() -> serviceB(args))
  .map(Optional::of).orElseGet(() -> serviceC(args))
  .map(Optional::of).orElseGet(() -> serviceD(args));

.map(func).orElseGet(sup)와 함께 사용하기에 매우 편리한 패턴입니다 Optional. "이것이 Optional가치를 포함 한다면 v, 나에게 func(v), 그렇지 않으면 나에게주세요 sup.get()"를 의미합니다.

이 경우에 우리는를 호출하고을 serviceA(args)얻는다 Optional<Result>. Optional값 이 포함되어 있으면 vget Optional.of(v)을 원하지만 비어 있으면 얻을 수 serviceB(args)있습니다. 더 많은 대안으로 헹구십시오.

이 패턴의 다른 용도는

  • .map(Stream::of).orElseGet(Stream::empty)
  • .map(Collections::singleton).orElseGet(Collections::emptySet)

1
허,이 전략을 사용할 때 일식은 "선택적 <String> 유형의 orElseGet (Supplier <? extends String>) 메소드는 인수 (()-> {})에 적용 할 수 없습니다"라고 말합니다. String에서 Optional을 유효한 전략으로 반환하는 것을 고려하십시오 ??
chrismarx

@chrismarx () -> {}는을 반환하지 않습니다 Optional. 무엇을 이루려고 노력하고 있습니까?
Misha

1
나는 단지 예제를 따르려고합니다. 지도 통화 연결이 작동하지 않습니다. 내 서비스는 문자열을 반환하며 물론 사용할 수있는 .map () 옵션이 없습니다.
chrismarx

1
곧 우수한 읽을 수있는 대안 or(Supplier<Optional<T>>)자바 (9)의
데이비드 M.

1
@Sheepy 당신은 착각합니다. .map()빈에서 Optional빈을 생성합니다 Optional.
Misha

27

아마도 이것은 당신이 추구하는 것입니다 : 하나의 옵션 또는 다른 옵션에서 가치 얻기

그렇지 않으면를 살펴볼 수 있습니다 Optional.orElseGet. 다음은 내가 생각 하는 것의 예입니다 .

result = Optional.ofNullable(serviceA().orElseGet(
                                 () -> serviceB().orElseGet(
                                     () -> serviceC().orElse(null))));

2
이것이 천재들이 일반적으로하는 일입니다. 옵션을 nullable로 평가하고 감싸는 ofNullable것은 내가 본 것 중 가장 멋진 것입니다.
Jin Kwon

5

여전히 JDK8을 사용한다고 가정하면 몇 가지 옵션이 있습니다.

옵션 # 1 : 나만의 도우미 방법 만들기

예 :

public class Optionals {
    static <T> Optional<T> or(Supplier<Optional<T>>... optionals) {
        return Arrays.stream(optionals)
                .map(Supplier::get)
                .filter(Optional::isPresent)
                .findFirst()
                .orElseGet(Optional::empty);
    }
}

당신이 할 수 있도록 :

return Optionals.or(
   ()-> serviceA(args),
   ()-> serviceB(args),
   ()-> serviceC(args),
   ()-> serviceD(args)
);

옵션 # 2 : 라이브러리 사용

예를 들어 google guava의 Optional은 올바른 or()작업 (JDK9와 동일)을 지원합니다. 예 :

return serviceA(args)
  .or(() -> serviceB(args))
  .or(() -> serviceC(args))
  .or(() -> serviceD(args));

(각 서비스가 com.google.common.base.Optional대신을 반환 하는 위치 java.util.Optional).


Optional<T>.or(Supplier<Optional<T>>)구아바 문서에서 찾지 못했습니다 . 당신은 그것에 대한 링크가 있습니까?
Tamas Hegedus

.orElseGet은 T를 반환하지만 Optional :: empty는 Optional <T>를 반환합니다. Optional.ofNullable (........ 또는 Else (null))은 @aioobe에 설명 된대로 원하는 효과를 갖습니다.
미겔 페레이라

@TamasHegedus 옵션 # 1을 의미합니까? 그것은 위에 스니핑 된 맞춤형 구현입니다. 추신 : 답변이 늦어 죄송합니다
Sheepy

@MiguelPereira .orElseGet이 경우 반환 옵션 <T> 때문에에 그것의 운영 옵션 <옵션 <T >>
Sheepy

2

이것은 패턴 일치에 적합하고 Somes 및 None 구현 (예 : Javaslang , FunctionalJava ) 또는 cyclops-react 의 게으른 Maybe 구현을 사용 하는보다 전통적인 옵션 인터페이스에 적합 합니다.이 라이브러리의 저자입니다.

함께 외눈 박이-반응 당신은 또한 구조 사용할 수있는 패턴 매칭을 JDK 유형에. 옵션의 경우 방문자 패턴을 통해 현재 및 부재 한 경우에 일치시킬 수 있습니다 . 다음과 같이 보일 것입니다-

  import static com.aol.cyclops.Matchables.optional;

  optional(serviceA(args)).visit(some -> some , 
                                 () -> optional(serviceB(args)).visit(some -> some,
                                                                      () -> serviceC(args)));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.