【问题标题】:How to execute logic on Optional if not present?如果不存在,如何在 Optional 上执行逻辑?
【发布时间】:2015-03-24 15:01:33
【问题描述】:

我想用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"));
}

【问题讨论】:

  • 如果不存在对象,你想从方法返回什么?
  • 我希望始终按照方法返回参数的指示返回Optional

标签: java java-8 optional


【解决方案1】:

对于 Java 9 或更高版本,ifPresentOrElse 很可能是您想要的:

Optional<> opt = dao.find();

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

使用vavr 或类似方法进行柯里化可能会得到更简洁的代码,但我还没有尝试过。

【讨论】:

  • 似乎应该包含在 v1 (Java 8) 中... 哦好吧...
  • 是的......我也认为他们实际上在 Java 8 中错过了这一点。还有更多......如果你想在值存在时做某事,他们给出了“ifPresent()”。如果您想在值存在时做某事而在值不存在时做其他事情,他们会给出“ifPresentOrElse(f1, f2)”。但是如果我只想在它不存在的情况下做一些事情,它仍然缺乏(像“ifNotPresent()”这样的东西是合适的)。使用 ifPresentOrElse 我不得不使用在后一种情况下什么都不做的 present 函数。
  • 如果你能介绍一个框架,看看Vavr(前Javaslang)和他们的Option,它有一个onEmpty method
  • 宁可使用 Java 9 或使用 if,否则。 vavr 不是很好
  • 这对于顺子来说太复杂了!
【解决方案2】:

我不认为你可以在一个单一的声明中做到这一点。最好这样做:

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

【讨论】:

  • 这可能是正确的答案,但它在哪些方面优于null 检查?从我的角度来看,没有orElse... 会更糟。
  • @DaRich,您可以忘记代码中间的空值,这会导致 NPE。但是您不能不小心忽略Optional,这始终是一个明确(且危险)的决定。
【解决方案3】:

对于 Java 8,Spring 提供了“使用 Optionals 的实用方法”中的 ifPresentOrElse 来实现您想要的。 例如:

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

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

【讨论】:

  • 有用的东西总是藏得很深,直到魔术师心情好。
【解决方案4】:

您必须将其拆分为多个语句。这是一种方法:

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));

【讨论】:

    【解决方案5】:

    一个.orElseRun 方法,但它被称为.orElseGet

    您的伪代码的主要问题是.isPresent 不返回Optional&lt;&gt;。但是.map 返回一个Optional&lt;&gt;,它具有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();
        });
    }
    

    但这比你以前的更笨重。

    【讨论】:

      【解决方案6】:

      首先,您的dao.find() 应该返回Optional&lt;Obj&gt;,否则您必须创建一个。

      例如

      Optional<Obj> = dao.find();
      

      或者你可以自己做:

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

      这个将返回Optional&lt;Obj&gt;(如果存在)或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;
              });
          }
      

      这是您正在寻找的一个班轮 :)

      【讨论】:

      • 返回 null 违背了 Optionals 的目的。在 OPs 问题中,如果找不到对象必须做什么是模棱两可的。恕我直言,最好返回一个新实例化的对象,也许 setAvailable 为 false。当然,这里的 OP 记录了致命的,这意味着他可能打算终止,所以这并不重要。
      • 这个解决方案返回一个Object,而最初的问题是一个返回Optional&lt;Object&gt;的方法。我的(较旧的)答案非常相似,但有所不同:stackoverflow.com/a/36681079/3854962
      • 为什么使用flatMap
      • 因为他返回的是一个可选项而不是一个值。 FlatMap 将 Optional> 转换为 Optional
      【解决方案7】:

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

      即相当于ifAbsent()ifNotPresent() 这里是对已经提供的出色答案的轻微修改。

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

      【讨论】:

      • ifPresentOrElse 需要 Java 9。
      【解决方案8】:

      标题:“如果 Optional 不存在,如何执行逻辑?”

      回答:

      使用orElseGet() 作为缺少ifNotPresent() 的解决方法。既然它希望我们返回一些东西,那就返回 null.

      Optional.empty().orElseGet(() -> {
          System.out.println("The object is not present");
          return null;
      });
      
      //output: The object is not present
      
      

      
      Optional.ofNullable(null).orElseGet(() -> {
          System.out.println("The object is not present");
          return null;
      });
      
      //output: The object is not present
      
      

      我还使用它来轻松实现 singleton pattern 延迟初始化。

      public class Settings {
          private Settings(){}    
          private static Settings instance;
          public static synchronized Settings getInstance(){
              Optional.ofNullable(instance).orElseGet(() -> instance = new Settings());
              return instance;
          } 
      }
      

      当然getInstance()的内容可以直接返回第一条语句写成一行,但是我想演示一下orElseGet()作为ifNotPresent()的使用。

      【讨论】:

        【解决方案9】:

        我想出了几个“单线”解决方案,例如:

            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 的选项是可以接受的。

        【讨论】:

          【解决方案10】:

          使用 Java 8 Optional 可以:

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

          【讨论】:

            【解决方案11】:

            您需要Optional.isPresent()orElse()。您的 sn-p 不会工作,因为如果不存在,它不会返回任何内容。

            Optional 的重点是从方法中返回它。

            【讨论】:

              【解决方案12】:

              ifPresentOrElse 也可以处理空指针的情况。简单的方法。

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

              【讨论】:

                【解决方案13】:

                我想你不能改变dao.find()方法来返回Optional&lt;Obj&gt;的一个实例,所以你必须自己创建一个合适的。

                以下代码应该可以帮助您。我创建了类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);
                      }
                    }
                  }
                }
                

                【讨论】:

                • 如果您投反对票,请发表评论。这有助于我改进答案。
                • 我同意反对者应该在这里发表评论。我认为这是因为我希望将 java7 重构为 java8 代码,而旧代码由 8 行组成。如果我用你的建议替换它,这对任何人都没有帮助,只会让事情变得更糟。
                • 我不明白你的意思。我确实将 java 7 重构为 8,不是吗?它会在多大程度上使事情变得更糟?我看不出这个解决方案有任何缺陷。有人可能会争论在 Optional 上设置 else (或解决方法)的全部意义是否有意义。但我正确回答了您的问题并提供了一个可行的示例。
                • 你的解决方案对我来说似乎完全有效,迈克。无论如何,引入像 OptionalAction 这样的显式类作为能够将代码移植到 java8 的解决方法似乎有点过度设计,如果在 java7 中这已经只是几个班轮。
                • 可选 object = (v == null) ? Optional.empty() : Optional.of(v);可以重写为: Optional object = Optional.ofNullable(v);
                猜你喜欢
                • 2019-09-12
                • 1970-01-01
                • 1970-01-01
                • 2010-09-19
                • 2017-05-29
                • 2017-03-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多