[java] Java 8-Optional.flatMap과 Optional.map의 차이점

무엇 이러한 두 가지 방법의 차이는 다음과 같습니다 Optional.flatMap()Optional.map()?

예를 들어 주시면 감사하겠습니다.



답변

사용 map하는 기능은 당신이 필요로하는 개체 또는 반환하는 경우 flatMap함수가 반환하는 경우를 Optional. 예를 들면 다음과 같습니다.

public static void main(String[] args) {
  Optional<String> s = Optional.of("input");
  System.out.println(s.map(Test::getOutput));
  System.out.println(s.flatMap(Test::getOutputOpt));
}

static String getOutput(String input) {
  return input == null ? null : "output for " + input;
}

static Optional<String> getOutputOpt(String input) {
  return input == null ? Optional.empty() : Optional.of("output for " + input);
}

두 인쇄 문 모두 같은 것을 인쇄합니다.


답변

둘 다 옵션 유형에서 무언가에 이르기까지 기능을 수행합니다.

map()당신이 가진 옵션에 “있는 그대로 “기능을 적용합니다 :

if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));

함수가 함수 인 경우 어떻게됩니까 T -> Optional<U>?
결과는 이제 Optional<Optional<U>>!

그게 무엇 flatMap()에 관한 것입니다 : 함수가 이미를 반환하는 경우 Optional, flatMap()조금 더 똑똑이며, 반환 이중 포장을하지 않습니다 Optional<U>.

두 개의 관용구 구성 : mapflatten.


답변

참고 :-아래는 맵 및 플랫 맵 기능의 예입니다. 그렇지 않은 경우 선택 사항은 기본적으로 반환 유형으로 만 사용하도록 설계되었습니다.

이미 알고 있듯이 Optional은 단일 객체를 포함하거나 포함하지 않을 수있는 일종의 컨테이너이므로 null 값을 예상하는 모든 위치에서 사용할 수 있습니다 (옵션을 올바르게 사용하면 NPE가 표시되지 않을 수 있음). 예를 들어, null을 허용 할 수있는 person 객체를 예상하는 메소드가있는 경우 다음과 같이 메소드를 작성하려고 할 수 있습니다.

void doSome(Optional<Person> person){
  /*and here you want to retrieve some property phone out of person
    you may write something like this:
  */
  Optional<String> phone = person.map((p)->p.getPhone());
  phone.ifPresent((ph)->dial(ph));
}
class Person{
  private String phone;
  //setter, getters
}

여기서는 선택적 유형으로 자동 래핑 된 문자열 유형을 반환했습니다.

개인 클래스가 다음과 같이 보이면 전화도 선택 사항입니다.

class Person{
  private Optional<String> phone;
  //setter,getter
}

이 경우 맵 함수를 호출하면 반환 된 값을 Optional에 래핑하고 다음과 같은 결과를 얻습니다.

Optional<Optional<String>> 
//And you may want Optional<String> instead, here comes flatMap

void doSome(Optional<Person> person){
  Optional<String> phone = person.flatMap((p)->p.getPhone());
  phone.ifPresent((ph)->dial(ph));
}

추신; NullPointerExceptions 없이는 살 수 없다면 isPresent ()로 확인하지 않고 Optional에서 get 메소드 (필요한 경우)를 호출하지 마십시오.


답변

도움이 된 것은 두 함수의 소스 코드를 살펴 보는 것입니다.

-결과를 선택 사항으로 묶습니다.

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
    }
}

flatMap- ‘원시’객체를 반환

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value)); //<---  returns 'raw' object
    }
}


답변

  • Optional.map():

모든 요소를 ​​취하고 값이 존재하면 함수에 전달됩니다.

Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);

이제 추가 된 세 가지 값 중 하나가 있습니다 true또는 false에 랩 선택 하면, optionalValue현재, 또는이었다 비어있는 옵션 이 없습니다.

을 사용할 수있는 결과를 처리 할 필요 ifPresent()가없는 경우 반환 값이 없습니다.

optionalValue.ifPresent(results::add); 
  • Optional.flatMap():

동일한 스트림 방법과 유사하게 작동합니다. 스트림 스트림을 평평하게합니다. 값이 표시되면 기능에 적용된다는 차이점이 있습니다. 그렇지 않으면 빈 옵션이 반환됩니다.

선택적 값 함수 호출을 작성하는 데 사용할 수 있습니다.

메소드가 있다고 가정하십시오.

public static Optional<Double> inverse(Double x) {
    return x == 0 ? Optional.empty() : Optional.of(1 / x);
}

public static Optional<Double> squareRoot(Double x) {
    return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}

그런 다음 다음과 같이 역의 제곱근을 계산할 수 있습니다.

Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);

또는 원하는 경우 :

Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);

중 하나 경우 inverse()또는 squareRoot()반환 Optional.empty(), 결과는 비어 있습니다.


답변

괜찮아. 당신은 당신이 중첩 된 선택적 개체에 직면 할 때 만 사용 ‘flatMap’필요 . 다음은 그 예입니다.

public class Person {

    private Optional<Car> optionalCar;

    public Optional<Car> getOptionalCar() {
        return optionalCar;
    }
}

public class Car {

    private Optional<Insurance> optionalInsurance;

    public Optional<Insurance> getOptionalInsurance() {
        return optionalInsurance;
    }
}

public class Insurance {

    private String name;

    public String getName() {
        return name;
    }

}

public class Test {

    // map cannot deal with nested Optionals
    public Optional<String> getCarInsuranceName(Person person) {
        return person.getOptionalCar()
                .map(Car::getOptionalInsurance) // ① leads to a Optional<Optional<Insurance>
                .map(Insurance::getName);       // ②
    }

}

Stream과 마찬가지로 Optional # map은 Optional에 의해 래핑 된 값을 반환합니다. 그래서 우리는 중첩 된 Optional-을 얻습니다 Optional<Optional<Insurance>. 그리고 ②에서, 우리는 그것을 보험 사례로 매핑하려고합니다. 그것이 비극이 일어난 방식입니다. 루트는 중첩 옵션입니다. 쉘에 관계없이 핵심 가치를 얻을 수 있다면 그것을 달성 할 수 있습니다. 이것이 flatMap이하는 일입니다.

public Optional<String> getCarInsuranceName(Person person) {
    return person.getOptionalCar()
                 .flatMap(Car::getOptionalInsurance)
                 .map(Insurance::getName);
}

결국, 나는 당신이 Java8을 체계적으로 공부하고 싶다면 Java 8 In Action 을 권장합니다.


답변