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


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.

의사 코드의 주요 문제 .isPresentOptional<>. 그러나 메서드 .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.