옵션이없는 경우 로직을 실행하는 방법은 무엇입니까?


83

java8을 사용하여 다음 코드를 바꾸고 싶습니다 Optional.

public Obj getObjectFromDB() {
    Obj obj = dao.find();
    if (obj != null) {
        obj.setAvailable(true);
    } else {
        logger.fatal("Object not available");
    }

    return obj;
}

다음 의사 코드는 orElseRun방법 이 없기 때문에 작동하지 않지만 어쨌든 내 목적을 보여줍니다.

public Optional<Obj> getObjectFromDB() {
    Optional<Obj> obj = dao.find();
    return obj.ifPresent(obj.setAvailable(true)).orElseRun(logger.fatal("Object not available"));
}

객체가없는 경우 메서드에서 무엇을 반환하고 싶습니까?
— Duncan Jones

Optional메서드 반환 매개 변수에 표시된대로 항상 반환하고 싶습니다 .
— membersound

답변:


124

Java 9 이상을 사용하면 다음과 같이 ifPresentOrElse할 수 있습니다.

Optional<> opt = dao.find();

opt.ifPresentOrElse(obj -> obj.setAvailable(true),
                    () -> logger.error("…"));

vavr 등을 사용하여 커링 하면 더 깔끔한 코드를 얻을 수 있지만 아직 시도하지 않았습니다.


63
V1에 포함되어 있어야합니다 뭔가 (자바 8) ... 것 같아 오 잘 ...
— ycomp

2
예 ... 또한 Java 8에서 실제로 놓친 것 같습니다. 그리고 더 ... 값이있을 때 무언가를하고 싶다면 "ifPresent ()"를주었습니다. 값이있을 때 뭔가를하고 그렇지 않을 때 다른 일을하고 싶다면 "ifPresentOrElse (f1, f2)"를주었습니다. 하지만 존재하지 않는 상태로만 무언가를하고 싶다면 여전히 부족합니다 ( "ifNotPresent ()"와 같은 것이 적합 할 것입니다). ifPresentOrElse를 사용하면 나중의 경우에는 아무것도하지 않는 현재 함수를 사용해야합니다.
— hbobenicio

프레임 워크를 소개 할 수 있다면 Vavr (이전 Javaslang) 및 해당 옵션을 살펴보면 onEmpty 메소드가 있습니다
— Andreas

오히려 Java 9를 사용하거나 if, else를 사용하십시오. vavr은 매우 좋은하지 않습니다
— senseiwu

이것은 똑바로 너무 복잡합니다.
— JBarros35

37

나는 당신이 하나의 진술로 그것을 할 수 있다고 생각하지 않습니다. 더 나은 방법 :

if (!obj.isPresent()) {
    logger.fatal(...);   
} else {
    obj.get().setAvailable(true);
}
return obj;

37
이것이 정답 일 수 있지만 어떤면에서 null수표 보다 우월 합니까? 내 관점에서 볼 때 orElse....
— DaRich

4
@DaRich, 코드 중간에 NPE가 발생하는 것을 잊어 버릴 수 있습니다. 그러나 Optional우연히 무시할 수는 없으며 항상 명시적이고 위험한 결정입니다.
— Dherik

17

Java 8 Spring은 ifPresentOrElse"옵션과 함께 작동하는 유틸리티 메소드"에서 제공 하여 원하는 것을 달성합니다. 예는 다음과 같습니다.

import static org.springframework.data.util.Optionals.ifPresentOrElse;    

ifPresentOrElse(dao.find(), obj -> obj.setAvailable(true), () -> logger.fatal("Object not available"));

11

이것을 여러 문장으로 분할해야합니다. 이를 수행하는 한 가지 방법은 다음과 같습니다.

if (!obj.isPresent()) {
  logger.fatal("Object not available");
}

obj.ifPresent(o -> o.setAvailable(true));
return obj;

또 다른 방법 (과도하게 설계되었을 수 있음)은 다음을 사용하는 것입니다 map.

if (!obj.isPresent()) {
  logger.fatal("Object not available");
}

return obj.map(o -> {o.setAvailable(true); return o;});

만약 obj.setAvailable편리하게 반환 obj, 다음 수 단순히 두 번째 예 :

if (!obj.isPresent()) {
  logger.fatal("Object not available");
}

return obj.map(o -> o.setAvailable(true));

9

우선, dao.find()를 반환 Optional<Obj>하거나 생성해야합니다.

예 :

Optional<Obj> = dao.find();

또는 다음과 같이 스스로 할 수 있습니다.

Optional<Obj> = Optional.ofNullable(dao.find());

이것은 Optional<Obj>존재하거나 Optional.empty()존재하지 않으면 반환 됩니다 .

이제 해결책을 찾아 보겠습니다.

public Obj getObjectFromDB() {
   return Optional.ofNullable(dao.find()).flatMap(ob -> {
            ob.setAvailable(true);
            return Optional.of(ob);    
        }).orElseGet(() -> {
            logger.fatal("Object not available");
            return null;
        });
    }

이것은 당신이 찾고있는 하나의 라이너입니다 :)


9
null을 반환하면 Optionals의 목적이 무효화됩니다. OPs 질문에서 객체가 발견되지 않으면 무엇을해야하는지 모호합니다. IMHO는 새로 인스턴스화 된 Object를 반환하는 것이 더 좋으며 아마도 setAvailable을 false로 설정합니다. 물론 여기에서 OP는 치명적인 기록을 남겼습니다. 즉, 그가 종료 할 계획이므로 실제로는 중요하지 않습니다.
— Somaiah Kumbera

이 솔루션은를 반환 Object하지만 원래 질문은 반환하는 메서드에 대한 것입니다 Optional<Object>. 내 (이전) 대답은 매우 유사하지만 다음과 같이 다릅니다. stackoverflow.com/a/36681079/3854962
— UTF_or_Death

왜 사용 flatMap합니까?
— Lino

값 대신 옵션을 반환하기 때문입니다. FlatMap은 Optional <Optional <X >>을 Optional <X>로 변환합니다
— Aman Garg

9

가 이다.orElseRun 방법은 있지만라고합니다 .orElseGet.

의사 코드의 주요 문제 .isPresent는 Optional<>. 그러나 메서드 .map가있는 Optional<>을 반환합니다 orElseRun.

한 문장에서 정말로 이것을하고 싶다면 이것은 가능하다 :

public Optional<Obj> getObjectFromDB() {
    return dao.find()
        .map( obj -> { 
            obj.setAvailable(true);
            return Optional.of(obj); 
         })
        .orElseGet( () -> {
            logger.fatal("Object not available"); 
            return Optional.empty();
    });
}

그러나 이것은 이전에 가졌던 것보다 훨씬 어색합니다.


3

예를 들어 다음과 같은 몇 가지 "한 줄"솔루션을 만들 수있었습니다.

    obj.map(o -> (Runnable) () -> o.setAvailable(true))
       .orElse(() -> logger.fatal("Object not available"))
       .run();

또는

    obj.map(o -> (Consumer<Object>) c -> o.setAvailable(true))
       .orElse(o -> logger.fatal("Object not available"))
       .accept(null);

또는

    obj.map(o -> (Supplier<Object>) () -> {
            o.setAvailable(true);
            return null;
    }).orElse(() () -> {
            logger.fatal("Object not available")
            return null;
    }).get();

처럼, 뭔가를 아주 좋은 보이지 않는 orElseRun더 나은 것,하지만 난 당신이 정말 한 줄 솔루션을 원하는 경우의 Runnable에 해당 옵션이 허용 생각합니다.


1

Java 8을 사용하면 Optional다음을 수행 할 수 있습니다.

    Optional<Obj> obj = dao.find();

    obj.map(obj.setAvailable(true)).orElseGet(() -> {
        logger.fatal("Object not available");
        return null;
    });

1

선택 사항이없는 경우에만 부작용을 실행하고 싶은 분

즉, ifAbsent()또는 ifNotPresent()여기에 이미 제공된 훌륭한 답변에 대한 약간의 수정이 있습니다.

myOptional.ifPresentOrElse(x -> {}, () -> {
  // logic goes here
})

1
ifPresentOrElse 자바 9. 필요
— JL_SO


0

ifPresentOrElse 는 널 포인터의 경우도 처리 할 수 ​​있습니다. 쉬운 접근.

   Optional.ofNullable(null)
            .ifPresentOrElse(name -> System.out.println("my name is "+ name),
                    ()->System.out.println("no name or was a null pointer"));

-2

dao.find()의 인스턴스를 반환하는 메서드를 변경할 수 없다고 가정 Optional<Obj>하므로 적절한 인스턴스를 직접 만들어야합니다.

다음 코드가 도움이 될 것입니다. OptionalActionif-else 메커니즘을 제공 하는 클래스를 만들었습니다 .

public class OptionalTest
{
  public static Optional<DbObject> getObjectFromDb()
  {
    // doa.find()
    DbObject v = find();

    // create appropriate Optional
    Optional<DbObject> object = Optional.ofNullable(v);

    // @formatter:off
    OptionalAction.
    ifPresent(object)
    .then(o -> o.setAvailable(true))
    .elseDo(o -> System.out.println("Fatal! Object not available!"));
    // @formatter:on
    return object;
  }

  public static void main(String[] args)
  {
    Optional<DbObject> object = getObjectFromDb();
    if (object.isPresent())
      System.out.println(object.get());
    else
      System.out.println("There is no object!");
  }

  // find may return null
  public static DbObject find()
  {
    return (Math.random() > 0.5) ? null : new DbObject();
  }

  static class DbObject
  {
    private boolean available = false;

    public boolean isAvailable()
    {
      return available;
    }

    public void setAvailable(boolean available)
    {
      this.available = available;
    }

    @Override
    public String toString()
    {
      return "DbObject [available=" + available + "]";
    }
  }

  static class OptionalAction
  {
    public static <T> IfAction<T> ifPresent(Optional<T> optional)
    {
      return new IfAction<>(optional);
    }

    private static class IfAction<T>
    {
      private final Optional<T> optional;

      public IfAction(Optional<T> optional)
      {
        this.optional = optional;
      }

      public ElseAction<T> then(Consumer<? super T> consumer)
      {
        if (optional.isPresent())
          consumer.accept(optional.get());
        return new ElseAction<>(optional);
      }
    }

    private static class ElseAction<T>
    {
      private final Optional<T> optional;

      public ElseAction(Optional<T> optional)
      {
        this.optional = optional;
      }

      public void elseDo(Consumer<? super T> consumer)
      {
        if (!optional.isPresent())
          consumer.accept(null);
      }
    }
  }
}

1
투표하지 않으시면 댓글을 남겨주세요. 이것은 답변을 개선하는 데 도움이됩니다.
— mike

나는 반대 투표자가 여기에 언급해야한다는 데 동의합니다. 이전 코드는 8 줄로 구성된 반면 java7을 java8 코드로 리팩터링하려고했기 때문이라고 생각합니다. 그리고 나는 경우 것입니다 귀하의 제안하고자하지 도움말 누구로 교체, 그러나 다만 상황이 악화.
— membersound

나는 당신의 요점을 이해하지 못합니다. Java 7에서 8로 리팩터링 했죠? 그리고 그것이 상황을 더 악화시킬 수 있습니까? 이 솔루션에는 결함이 없습니다. 다른 사람 (또는 해결 방법)에 대한 전체 요점이 합당한 지 여부를 논할 수 Optional있습니다. 그러나 나는 귀하의 질문에 올바르게 대답하고 실제 사례를 제공했습니다.
— 마이크

당신의 해결책은 나에게 완전히 타당 해 보입니다, 마이크. 어쨌든 OptionalAction코드를 java8로 이식 할 수있는 해결 방법 과 같은 명시 적 클래스를 도입 하는 것은 약간 과잉 엔지니어링 된 것 같습니다.
— membersound

2
Optional <DbObject> 개체 = (v == null)? Optional.empty () : Optional.of (v); 다시 작성할 수 있습니다. Optional <DbObject> object = Optional.ofNullable (v);
— Geir
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.