【问题标题】:Java: Elegantly returning an Object in an Optional<Object> if present?Java:如果存在,优雅地返回 Optional<Object> 中的对象?
【发布时间】:2020-07-02 19:22:40
【问题描述】:

目前,我有以下有效的代码:


    //in class Bar
        
        public Foo getFooIfItIsPresent(String param) {
                Optional<Foo> result = loadOptionalFoo(param);
                if (result.isPresent()) {
                    return result.get();
                } else {
                    return null;
                }
        
    // main driver code
    
    Foo foo = Bar.getFooIfItIsPresent(param);
                        
    if (foo != null) {
       // Currently just print, but might want to do stuff like pass foo to another object, etc.
       System.out.println(foo.getSomething() + foo.getSomethingElse());
    }

这有点难看,因为我正在显式检查 null;此外,我还有一个复杂的函数getFooIfItIsPresent,其存在的唯一目的是isPresent() 舞蹈。我想做类似的事情:


    Bar.loadOptionalFoo(param).ifPresent((foo) -> {
    // Print, or whatever I wanna do!
    System.out.println(foo.getSomething() + foo.getSomethingElse());
    });

我知道这不会编译。有一个非常 similar 的问题,我尝试了很多东西,但编译器抱怨。例如:


    Bar.loadOptionalFoo(week).map(foo -> {
    // Print, or whatever I wanna do!
                                System.out.println(foo.getSomething() + foo.getSomethingElse());
                            }).filter(Objects::nonNull);

是的,上面的代码是荒谬的,但我似乎无法为此找到一个优雅的解决方案,因此非常感谢您的帮助!

【问题讨论】:

  • 空对象模式会是一种值得追求的方法吗? en.wikipedia.org/wiki/Null_object_pattern
  • 这里的编译错误是什么?您似乎没有解析错误,所以我认为您的 lambda 与 Consumer 接口不兼容?
  • 是的,基本上。这是错误的 sn-p:reason: cannot infer type-variable(s) U (argument mismatch; bad return type in lambda expression missing return value)
  • @BrentWorden 实际上这可能适合这里。我的 Foo 是 Optional 的原因是因为它在加载它时可能只是空的(而不是不存在),这是我从阅读维基百科链接中理解的?在这种情况下,您是否建议检查 null 是可以接受的?
  • True: map() 接受一个 Function 并且你的 lambda 没有返回,但我没有看到你的情况的重点:你没有试图处理“如果不是”那里”的一部分。

标签: java


【解决方案1】:

tl;博士

您显示的代码“我知道这不能编译”实际上应该编译。该代码是您的解决方案。

使用类似的代码,看这个方法:

public Optional < DayOfWeek > getFavoriteDayOfWeek ( )
{
    return Optional.ofNullable( DayOfWeek.WEDNESDAY );  // Silly implementation for demonstration.
}

…这样称呼:

this.getFavoriteDayOfWeek().ifPresent(
        ( DayOfWeek dow ) -> { … dow.get() …  }
);

…运行live successfully at IdeOne.com

请记住,Optional 是它自己的对象,包装了一些其他对象作为其有效负载。所以要小心你的类型声明:Optional&lt;DayOfWeek&gt; someVarDayOfWeek someVar

返回Optional

如果 null 在您的域中是可接受的值,则返回 Optional。返回从Optional 中解包的null 违背了Optional 的目的。

purpose of an Optional 是向调用方法发出信号,表明 null 确实是一种有效的可能性。 Java 的类型系统被用来提醒调用程序员编写null 的可能性。一个Optional 对象就像一个大的safety-orange 交通标志,上面写着:“当心:前面可能是NULL”。

仅返回从 Optional 中解包的 null 不会增加任何价值,并且会产生脆弱的代码。

问题中显示的代码不必要地跳过了额外的箍,只是引入了额外的间接级别而没有任何好处。如果null 的有效负载是有效结果,则调用方法确实应该收到Optional

所以这个:

//in class Bar
    
    public Foo getFooIfItIsPresent(String param) {
            Optional<Foo> result = loadOptionalFoo(param);
            if (result.isPresent()) {
                return result.get();
            } else {
                return null;
            }
    
// main driver code

Foo foo = Bar.getFooIfItIsPresent(param);
                    
if (foo != null) {
   // Currently just print, but might want to do stuff like pass foo to another object, etc.
   System.out.println(foo.getSomething() + foo.getSomethingElse());
}

……应该是:

//in class Bar
    
// ➥ Delete this method `getFooIfItIsPresent`. Adds no value.
//     public Foo getFooIfItIsPresent(String param) 

……和……

// main driver code

Optional<Foo> result = someBar.loadOptionalFoo( param );
                    
if ( result.isPresent() ) {
   // Currently just print, but might want to do stuff like pass foo to another object, etc.
   Foo foo = result.get() ;  // Calling `get` is safe because we checked for null in the `if … isPresent` line above.
   System.out.println( foo.getSomething() + foo.getSomethingElse() ) ;
}

请注意我们如何调用Optional::get,仅在调用Optional::ifPresent 来检查null 之后。

如果您想要解决Optional 为空且不存在Foo 对象的if else 条件,Optional 类提供了多种方法。参见orElseorElseGetorElseThrow等方法。

您的有问题的代码确实应该编译

你说这段代码不能编译:

Bar.loadOptionalFoo(param).ifPresent((foo) -> {
// Print, or whatever I wanna do!
System.out.println(foo.getSomething() + foo.getSomethingElse());
});

实际上,该代码应该可以编译。这样做是完全合理的,这是我在上面展示的解决方案的另一种变体。

这是一个类似的例子。我使用Java内置的DayOfWeek枚举类创建了getFavoriteDayOfWeek方法,它返回一个Optional&lt; DayOfWeek &gt;。如果注册了最喜欢的一天,则该方法返回一个Optional,其中包含一个DayOfWeek 对象。如果尚未确定收藏夹,则该方法返回一个空的Optional。这是该方法的虚拟版本。

public Optional < DayOfWeek > getFavoriteDayOfWeek ( )
{
    return Optional.ofNullable( DayOfWeek.WEDNESDAY );
}

使用与您的代码类似的代码调用该方法:

this.getFavoriteDayOfWeek().ifPresent(
        ( DayOfWeek dayOfWeek ) -> {
            // Print, or whatever I wanna do!
            System.out.println( dayOfWeek.getDisplayName( TextStyle.FULL , Locale.CANADA_FRENCH ) );
        }
);

运行时:

默契

INFO - 已完成运行演示。

您可以将WEDNESDAY 对象更改为null 以进行实验。

public Optional < DayOfWeek > getFavoriteDayOfWeek ( )
{
    return Optional.ofNullable( null  );
}

运行时,我们看到 System.out.println( dayOfWeek.getDisplayName(… 代码从未被调用,因为我们的 Optional&lt;DayOfWeek&gt; 实际上包含 DayOfWeek 对象 (ifPresent) 的条件测试未得到满足。我们只能看到第二个System.out.println 的输出,即“Done running”。

INFO - 已完成运行演示。

看到这个code run live at IdeOne.com

制作Optional

您的问题的主体似乎在询问从Optional 中提取值。我在上面展示了。

您的问题的标题似乎是关于将值包装在 Optional 中。

  • 如果您知道要返回 null,请返回 Optional.empty()
  • 如果返回的内容可能为 null,也可能不为 null,请返回 Optional.ofNullable( x )
  • 如果在这种情况下返回的东西绝对为空,则返回Optional.of( x )。如果值为 null,则该方法将抛出 NullPointerException。当 null 的存在意味着您的应用出现严重问题时,请使用此方法。

顺便说一下,在看到您使用if (foo != null) { 之后的提示:

如果您确实需要测试null,我建议您不要使用x != nullx == null,而是分别使用Objects 类方法Objects.nonNull​( Object obj )Objects.isNull( Object obj )

更好的是,在无法容忍 null 存在的情况下,这意味着您的代码中出现重大意外故障,请致电 Objects.requireNonNull​( T obj )。如果接收到 null,则此方法抛出 NullPointerException。更好的是,此方法返回传递的对象。这使得它非常适合将传递的参数分配给其他变量。

public void doSomething( final Fruit fruit , final DayOfWeek dayOfWeek ) 
{
    this.fruit = Objects.requireNonNull( fruit ) ;
    this.dayOfWeek = Objects.requireNonNull( dayOfWeek ) ;
}

最后提示:Java 14 带来了更多有用的NullPointerException 对象,更好地定位故障位置。见JEP 358: Helpful NullPointerExceptions

【讨论】:

  • 实际上,“有问题的”代码在我检查时确实会编译。回顾版本控制,似乎我混淆了 lambda 参数作为另一个 Optional 再次,这导致编译错误并让我失望。我现在觉得很傻:(话虽如此,我认为您的回答对正确使用Optional 的目的和做法给出了最好的解释,所以谢谢!
【解决方案2】:

我无法理解您对 ifPresent 的问题,因为您甚至没有处理值为空的情况:为什么不使用方法引用?

让一些方法接受Foo 参数并执行任何操作:

class X {

  private Optional<Foo> loadFoo(String name) {...}
  private void consumeFoo(Foo foo) {...}

  void doSomething(String name) {
    this.loadFoo(name).ifPresent(this::consumeFoo);
  }
}

你也可以这样做

return result.orElse(null);

代替:

        if (result.isPresent()) {
            return result.get();
        } else {
            return null;
        }
    

【讨论】:

  • 你说得对,会员参考是我逃避的问题,是一个很好的解决方案,感谢您的时间!
【解决方案3】:

我同意 Basil 的观点,但还有另一种可能性:

Bar.loadOptionalFoo(week)
    .map(foo -> 
            String.format("%s %s",
            foo.getSomething(),
            foo.getSomethingElse()))
   .ifPresent(System.out::println);

【讨论】:

    【解决方案4】:

    您可以更优雅地直接返回null,然后进行空检查:)。或者,如果您不想检查null,则可以返回Optional 并进行isPresent 检查

    public Foo getFoo(String param) {
                return Optional.ofNullable(loadOptionalFoo(param)).orElse(null);
    }
    
    Foo foo = Bar.getFoo(param);
                        
    if (foo != null) {
       // Currently just print, but might want to do stuff like pass foo to another object, etc.
       System.out.println(foo.getSomething() + foo.getSomethingElse());
    }
    

    【讨论】:

      猜你喜欢
      • 2019-07-20
      • 2013-09-18
      • 2019-06-21
      • 1970-01-01
      • 2019-04-12
      • 2021-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多