可选isPresent vs orElse(null)

Sad*_*Ali 17 java java-8 spring-data-jpa

我在我的项目中更新了Spring 8的依赖项,并被编译错误轰炸,其中方法定义findOne()已被替换findById(),现在返回一个Optional(如果我错了,请纠正我).

在重构时,我遇到了多种我可以选择采用的方法,因此我想要一些关于哪一种是首选的输入.

第一种方法:

ExpectedPackage ep = expectedPackageRepository.findById(1).orElse(null);
if(ep != null){
    ep.setDateModified(new Date());
    expectedPackageRepository.saveAndFlush(ep);
}
Run Code Online (Sandbox Code Playgroud)

第二种方法:

Optional<ExpectedPackage> ep = expectedPackageRepository.findById(1);
if(ep.isPresent()){
    ep.get().setDateModified(new Date());
    expectedPackageRepository.saveAndFlush(ep.get());
}
Run Code Online (Sandbox Code Playgroud)

或者,我错过了第三种更好的方法吗?我经历了几个问题和一些文章,但我没有找到明确的答案.

Pog*_*ger 22

你也可以这样做:

expectedPackageRepository.findById(1).ifPresent(
    ep -> {
        ep.setDateModified(new Date());
        expectedPackageRepository.saveAndFlush(ep);
    }
);
Run Code Online (Sandbox Code Playgroud)

理想情况下,您还可以将方括号({})之间的部分提取到单独的方法中.然后,你可以像这样写:

    expectedPackageRepository.findById(1).ifPresent(this::doSomethingWithEp);
Run Code Online (Sandbox Code Playgroud)

哪里:

void doSomethingWithEp(ExpectedPackage ep) {
    ep.setDateModified(new Date());
    expectedPackageRepository.saveAndFlush(ep);
}
Run Code Online (Sandbox Code Playgroud)

你可以阅读ifPresent这里的文档:https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#ifPresent-java.util.function.Consumer-

如它所述,它将在值存在时执行指定的操作,否则不执行任何操作.

  • 如果你在检查存在后调用它会是危险的吗?我错过了什么吗? (3认同)
  • 正如Poger所说.关于ifPresent的好处是你不必调用"危险"的get方法,而是向你呈现值. (2认同)
  • 如果您在检查“isPresent()”后立即调用“get()”,那么应该没问题。我提出的方法在你的情况下看起来更干净。 (2认同)
  • 如果你应用正确的事务/会话边界(例如@Transactional注释),那么你不需要在存储库上调用save方法,使代码更简单 (2认同)
  • 经验法则是:避免`isPresent`和`get`.这个答案表明有更清晰的替代方案,而且最常见的情况是这种情况(尽管并非总是如此). (2认同)

Eug*_*ene 6

另一个答案基本上是你的第二种方法的重构,这本身没什么不妥,只是风格问题.当然,链接和提取到一个单独的方法将使这更加可读和清晰,毫无疑问(+1来自我),特别是因为正确使用ifPresent.

我只是在这里添加get,好吧,被视为某种设计错误(或者可能是一个糟糕的方法名称,可能来自guava心态).使用get即使它记录为在缺少该值时抛出异常也有点奇怪(如果你认为这里有getter,你就不会指望getter抛出异常).并且你不会期望之后get需要调用,至少在第一次交互时不需要调用.因此被提议被弃用(并且希望被删除),因此java-10添加了一个更好的补充- 这在您阅读之后就有意义,因为抛出部分是在方法的名称中,所以没有惊喜. isPresentOptionalgetorElseThrow()

此外,有人应该告诉你new Date(),当与Optionaljava-8 一起使用时,使用它只是看起来很奇怪,已经有更好的时间/日期相关类.

我也不太确定为什么要手动更新修改日期,当有弹簧注释时PreUpdate/PrePersist.

  • Brian Goetz承认命名`Optional :: get`方法是错误的.应该被命名为`Optional :: getOrThrowSomeHorribleIfTheThingIsEmpty`.http://i.stack.imgur.com/LPg1Z.png (5认同)

Bas*_*que 5

是的,还有其他方法。

如果您绝对希望始终有一个值,则Optional::orElseThrow在出现null时使用引发Exception。

如果您期望null可能到达,并有一个替代实例作为后备选项,请使用Optional::orElse

如果没有备用实例,但是您有一个函数可以调用以提供备用实例,请使用Optional::orElseGet

如果您不关心接收null,并且不想在null到达时执行任何操作,请使用Optional::ifPresent。如果值到达,则传递要运行的代码块。

如果您只关心到达的值是否满足某些要求,请使用Optional::filter。通过Predicate定义您的要求。例如,仅当一个Optional< String >包含文本该文本中包含单词purple时,我们才在乎myOptional.filter( s -> s.contains( "purple" ) ).ifPresent( this::print ) ;。如果收到null,则我们所需的操作(print在此示例中为)永远不会发生。如果接收到一个值但不满足我们的谓词,则我们期望的操作将永远不会发生。


这样做if( myOptional.isPresent() ) { SomeClass x = myOptional.get() ; … }是有效且安全的。但这不是原始意图,Optional因为它与进行老式的null检查基本相同if ( null == x ) { … }。其他方法Optional提供了一种更清晰,更优雅的方式来表达您对可能的零到达的意图。