如果不存在,如何在Optional上执行逻辑?


81

我想使用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总是按方法return参数指示的那样返回。
–membersound

Answers:


122

对于Java 9或更高版本,ifPresentOrElse最可能需要的是:

Optional<> opt = dao.find();

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

使用vavr之类的方法进行固化可能会得到更整洁的代码,但是我还没有尝试过。


61
似乎应该在v1(Java 8)中包含某些内容...哦,好吧
ycomp

2
是的...我也认为他们实际上错过了Java 8的使用。还有更多...如果您想在值存在时做点什么,他们给了“ ifPresent()”。如果要在存在值的情况下执行某些操作,而在不存在该值的情况下执行其他操作,则会给出“ ifPresentOrElse(f1,f2)”。但是,如果我只想做一些不存在的事情,它仍然缺乏(比如“ ifNotPresent()”这样的方法是合适的)。使用ifPresentOrElse时,我不得不使用当前函数,在以后的情况下不执行任何操作。
hbobenicio

如果您可以介绍一个框架,请看一下Vavr(以前的Javaslang)及其Option,它有一个onEmpty方法
Andreas

而是使用Java 9或使用if否则。vavr不太好
Senseiwu”,

如果不是这样的话,这太复杂了!
JBarros35

36

我认为您无法在一个声明中做到这一点。最好做:

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

36
这可能是正确的答案,但是在什么方面优于null支票?从我的角度来看,没有保险更糟orElse...
达里奇

4
@DaRich,您可以忘记代码中间的空值,这会导致NPE。但是您不能无意间忽略Optional它,它总是一个明确的(也是危险的)决定。
Dherik '18

15

对于Java 8,Spring提供ifPresentOrElse了“实用程序与Optionals一起使用”的功能,以实现所需的功能。例如:

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的目的。在OP问题中,如果找不到对象,该怎么办是模棱两可的。恕我直言,最好返回一个新实例化的Object,也许将setAvailable设置为false。当然,这里的OP已经致命,这意味着他可能打算终止合同,所以这并不重要。
Somaiah Kumbera '16

此解决方案返回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

对于只在没有可选项的情况下要执行副作用的那些人

即,等同于ifAbsent()ifNotPresent()此处是对已经提供的好答案的略微修改。

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

1
ifPresentOrElse需要Java 9
JL_SO


0

使用Java 8Optional可以通过以下方式完成:

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

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

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>,因此您必须自己创建一个合适的实例。

以下代码将为您提供帮助。我创建了类OptionalAction,该类为您提供了if-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

我同意下降投票者应在此处发表评论。我认为这是因为我正在试图将java7重构为java8代码,而旧代码却由8行组成。如果我将其替换为您的建议,那对任何人都没有帮助,只会使事情变得更糟。
Memberound

我不明白你的意思。我将Java 7重构为8,不是吗?到什么程度它会使情况变得更糟?我看不到此解决方案有任何缺陷。有人可能会争论说采用其他方法(或解决方法)的全部意义是否Optional合理。但我正确回答了您的问题,并提供了一个有效的示例。
mike

迈克,您的解决方案对我来说似乎完全有效。无论如何,引入显式类(例如OptionalAction作为一种变通方法,因为能够将代码移植到java8)似乎有点过分设计,如果在java7中,这已经仅仅是一些衬里了。
–membersound

2
Optional <DbObject>对象=(v ==空)Optional.empty():可选.of(v); 可以重写为:Optional <DbObject> object = Optional.ofNullable(v);
盖尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.