如何使用 Java Optional 优雅地替换三元运算符

Posted

技术标签:

【中文标题】如何使用 Java Optional 优雅地替换三元运算符【英文标题】:How to use Java Optional to elegantly replace Ternary operators 【发布时间】:2019-04-12 12:37:13 【问题描述】:

一个超级简单的问题:

这是我使用传统三元运算符 ? 的纯 Java 代码

public DateTime getCreatedAt() 
    return !recordA.isPresent() ? recordB.get().getCreatedAt() : recordA.get().getCreatedAt();

我最好的选择是:

public DateTime getCreatedAt() 
    return recordA.map(
        record -> record.getCreatedAt())
        .orElse(recordB.get().getCreatedAt());
  

这可以编译,但看起来它的行为不正确。 它总是执行两个分支,例如当recordA isPresent() 时,它仍然执行recordB.get().getCreatedAt() 这抛出了我

java.util.NoSuchElementException: No value present

感谢任何帮助!

基本上,我想用更高级的 Optional/lamda 功能替换传统的三元运算符。

【问题讨论】:

.orElseGet(() -> recordB.get().getCreatedAt()); ? 你需要orElseGet。您还可以使用两个选项和or() recordB 也是可选的吗? 看我的回答如果它是空的如何处理。 Optional 不应该以这种方式使用。我的意思是,如果你愿意,你可以像这样使用它,毕竟它是你的代码 :) 但我的意思是,它不是为了替换三元运算符或 if/else 语句而设计的。它的主要用途是作为一个返回类型,在 youtube Stuart Marks 的会议上讨论这个话题。您可以像这样改进您的实际代码:return (recordA.isPresent() ? recordA : recordB).get().getCreatedAt() 【参考方案1】:

为避免急切地评估 else 分支,请使用 orElseGet,它采用函数接口 Supplier 的实例:

return recordA.map(
    record -> record.getCreatedAt())
    .orElseGet(() -> recordB.get().getCreatedAt());

【讨论】:

如果recordArecordB属于同一类型,可以简写为recordA.orElseGet(recordB::get).getCreatedAt() @Andreas 在recordB 为空Optional 时不起作用。 @tsolakp 它与问题和这个答案完全相同,即如果recordArecordB 都是空的,所有3 个都会抛出异常。 @Andreas。如果recordArecordB 为空,则不确定OP 是否想要异常。 @tsolakp 但是你为什么要解决我的评论,好像我评论的代码是唯一有问题的代码?也解决答案,因为它有同样的问题。更好的是,对问题写评论,通知 OP 潜在问题,并询问它是否是一个问题。也许逻辑是它们中至少有一个是非空的,这使它成为非问题。这当然不是我建议的较短代码引入的问题,就像我写 orElse(recordB.get()) 时那样,但这不是我写的。【参考方案2】:

我关于recordBOptional 的问题没有得到解答,但如果它是Optional,那么你不能安全地调用它的get 方法,你需要检查它是否为空。如果recordArecordB 都为空Otionals,则此处安全调用以获取记录或null。

        recordA
            .map(Record::getCreatedAt)
            .orElseGet( () -> recordB.map(Record::getCreatedAt).orElse(null) );

【讨论】:

我在第二部分看不到Optional 的用途。创建一个Optional 只是为了调用orElse(null) 是一种已知的反模式。 我同意。但是我们正在 OP 提供的示例中工作。理想情况下,getCreatedAt 也应该返回Optional,或者在我的回答中至少使用DateTime 的某种默认值而不是null【参考方案3】:

您正在寻找.orElseGet(() -> recordB.get().getCreatedAt());,可以在这篇文章中找到原因 --> Difference between Optional.orElse() and Optional.orElseGet()

有些人可能会觉得它有点主观,但我个人认为,默认情况下,每次使用 orElseGet() 比使用 orElse 更有意义,除非默认对象已经构造为这将防止您现在面临的许多意外问题(假设您没有阅读 orElse 和 orElseGet doc 之间的区别)。

阅读更多来自Java Optional – orElse() vs orElseGet()

【讨论】:

【参考方案4】:

如果您使用的是 Java 9+,则可以使用 ifPresentOrElse() 方法 as more completely explained in this *** answer。

recordA.ifPresentOrElse(
   value -> recordA.get().getCreatedAt(),
   () -> recordB.get().getCreatedAt()
);

【讨论】:

以上是关于如何使用 Java Optional 优雅地替换三元运算符的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 Optional:优雅地避免 NPE

没用 Java 8,怎么优雅地避免空指针?

没用 Java 8,怎么优雅地避免空指针?

没用 Java 8,怎么优雅地避免空指针?

使用Optional更优雅地处理非空判断

使用Optional更优雅地处理非空判断