保存Optional,停止使用isPresent

大多数函数式编程语言都提供了一个称为选项要么也许 to deal with the presence要么absence of a value, thus avoiding 空值。 Java 8引入java.util.选项al,是也许Java开发人员的类型。 遗憾的是,由于其灵活性,选项al is often misused, be it because the developer does not understand its power,要么be due to lack of background in functional programming.

在这篇文章中,我想强调一种滥用的常见模式可选的以及如何解决。

Note that instead of java.util.Optional, I will use the Vavr Option instead. Vavr is a lightweight library that brings Scala-like features to Java 8 projects. It focuses on providing a great developer experience both through consistent APIs and extensive documentation. See this short overview of how and why optional can help you. Head over to http://vavr.io if you want to know more.

但是,这里的所有内容都适用于任一实现。

A real world example

我想从一个可以用作重构候选的典型示例开始。

让我们考虑以下用例:使用存储库加载用户。 如果找到用户,则检查地址是否已设置,如果已设置,则返回地址的街道,否则返回空字符串。

使用空值我们编写类似于以下代码:

User user = repo.findOne("id");
if (user != null) {
  Address address = user.getAddress();
  if (null != address) {
    return address.getStreet();
  }
  else {
    return "";
  }
}

哎呀 这就是我所说的级联的耻辱桩。

解决这个问题很容易,只需使用选项:

Option<User> opt = Option.of(user);

if (opt.isPresent()) {
  Option<Address> address = Option.of(user.getAddress());
  if (address.isPresent()) {
    return address.get().getStreet();
  }
  else {
    return "";
  }
}

对?

错误! 每一次选项像这样使用时,微服务会在生产中死亡。 此修复程序基本上与上面相同。 相同的复杂度,相同的_级联的耻辱桩。

相反,我们使用地图操作员。

Map - the Swiss army knife of functional programming

地图使用时是你的朋友选项。 考虑到选项作为一个精美的礼品盒,里面装有一些东西。

假设您是一个优秀的程序员,并且编写了代码Test-First。 您会得到一个带袜子的礼物盒。

Gift box

但是谁想要袜子? 你想要一个球。 那么你地图礼品盒的功能袜子并将它们转换为球。 然后将结果放入新的礼品盒中。 单子的力量可以拯救您的生日。

Mapping to a ball

如果您是一个糟糕的编码者,并且根本不编写单元测试该怎么办? 好吧,那你就不会得到漂亮的袜子了。 但地图仍然可以正常工作:

Nothing from nothing

如果礼品盒是空的,那么地图甚至不会应用该功能。 因此,基本上它是“一无所有”。

Fixing things

回到原始问题,让我们使用选项。

User user = repo.findOne("id");
if (user != null) {
  Address address = user.getAddress();
  if (null != address) {
    return address.getStreet();
  }
}

首先,让找一个返回Option<User>代替空值:

Option<User> user = repo.findOne("id");
...

由于用户的地址是可选的(请参阅我在此处所做的操作;)用户#getAddress应该回来Option<Address>。 这将导致以下代码:

Option<User> user = repo.findOne("id");
user.flatMap(User::getAddress)
...

为什么flatMap...嗯,我将其保留为练习。 只要记住,用户#getAddress返回Option<Address>想想看,如果您使用地图?

现在我们有了Option<Address>我们可以地图再次:

Option<User> user = repo.findOne("id");
user.flatMap(User::getAddress)
    .map(Address::getStreet)
...

最后,我们只需要决定如果其他所有方法都失败了该怎么办:

Option<User> user = repo.findOne("id");
user.flatMap(User::getAddress)
    .map(Address::getStreet)
    .getOrElse("");

剩下的就是最终版本:

repo.findOne("id")
    .flatMap(User::getAddress)
    .map(Address::getStreet)
    .getOrElse("");

如果您从上到下阅读它,那么它就照原样了。

repo.findOne("id")                // Find a user
    .flatMap(User::getAddress)    //   if an address is available
    .map(Address::getStreet)      //   fetch the addresses street
    .getOrElse("");               //   otherwise use the empty string

Summary

我希望这篇简短的帖子能够说明Vavr及其功能的实用性选项抽象。 如果您只记得一件事,那就顺其自然不使用选项#isPresent要么选项#get,地图是你的朋友。

Vavr作为一个库,为Java中的对象功能编程提供了许多惊人的扩展,甚至对于棕地项目也是如此。 您可以在有意义的地方利用它的实用程序,而不必迁移到Scala或类似平台上,以至少从功能编程中获得一些好处。

当然,这都是语法糖。 但是,正如任何好的库一样,Vavr可以修复问题,而核心JDK不会轻易处理而不破坏大量代码。

将来的文章将介绍它的其他惊人功能,例如模式匹配,基于属性的测试,集合和其他功能增强。

from: https://dev.to//koenighotze/safe-the-optional-stop-using-ispresent