optionals

我们中任何使用允许空引用的语言进行编程的人,都将在尝试取消引用一个引用时经历过。 无论是导致segfault还是NullPointerException,它始终是一个错误。 托尼·霍尔将其描述为他十亿美元的错误 。 当函数向客户端的开发人员未预料到的客户端返回空引用时,通常会发生此问题。 用这样的代码说:

User user = userRepository.find("Alice");

精明的程序员会立即询问没有找到匹配“ Alice”的用户,但是find()方法的签名中没有任何内容告诉您期望什么。 过去,典型的Java解决方案是使该方法引发一个已检查的异常,也许是UserNotFoundException 。 这肯定会告知客户程序员可能会发生这种情况,但是它无助于增强其代码的表达能力。 捕获异常会导致妨碍理解的代码。 无论如何,受检查的异常都不受欢迎,人们不再倾向于编写引发异常的代码。

许多程序员将改为抛出未经检查的异常或返回空引用。 两者彼此一样坏,并且出于相同的原因:它们都不通知程序员期望这种可能性,并且如果处理不当,它们都将导致运行时失败。 Java 8引入了Optional类型来处理此问题。 每当编写可能返回或可能不返回值的方法时,都应使该方法返回希望返回的任何类型的Optional。 因此,在上面的示例中,find将返回Optional<User>类型的值。 客户端代码现在需要执行其他步骤来测试的存在,然后获取值:

Optional<User> userOpt = userRepository.find("Alice");
if (userOpt.isPresent()) {User user = userOpt.get();
}

此外,如果代码不加保护地调用get()则其IDE可能会警告他们。

Lambdas使事情变得更好

这个解决方案已经好很多了,但是Optional远不止于此:如果您坚持以这种方式处理可选内容,那么您将失去一些使代码更具表现力的机会。

上面的代码段改编自我自己对Codurance用于测试求职者的“社交网络”练习的实现。 我的实际代码更像是:

Optional<User> userOpt = userRepository.find(subject);
if (userOpt.isPresent()) {User user = userOpt.get();printAllMessagesPostedToUser(user);
}

Optional有一个ifPresent()方法,该方法允许我们提供一个Consumer ,如果存在Optional,它将被调用。 消费者的参数将是由可选参数包装的对象。 这使我们可以像这样重写代码:

userRepository.find(subject).ifPresent(user -> printAllMessagesPostedToUser(user));

实际上,我们可以更进一步,并用方法引用代替lambda:

userRepository.find(subject).ifPresent(this::printAllMessagesPostedToUser);

我认为这比if语句更清楚地传达了程序员的意图(在本例中为我的意图)。

令人疯狂的是,没有ifNotPresent()对应项,即使存在,也没有ifPresent是一个void方法,因此它们无论如何都无法链接。 Java 9通过其ifPresentOrElse(Consumer<T>, Runnable)方法来解决此问题,但是它仍然不是理想的选择。

替换默认值

关于不存在可选值的问题,我们该怎么办? 如果忘记了缺少功能的抱怨, ifPresent()仅适用于具有副作用的命令。 如果要实现查询,则可能需要用默认值替换为空的可选值,例如:

if (optionalValue.isPresent()) {return optionalValue.get();
}
return defaultValue;

使用Optional.orElse()可以很容易地做到这一点:

return optionalValue.orElse(defaultValue);

当您必须调用可能返回null且不受您控制的方法时,这也提供了一种方便的方法,可以将值清零。 之前,我们都有所有与此类似的书面代码:

value = methodThatMayReturnNull();
if (value == null) {value = defaultValue;
}

您可以使用Optional.ofNullable()重构该代码,因为如果该值为null,它将返回Optional.empty()

value = Optional.ofNullable(methodThatMayReturnNull()).orElse(defaultValue);

我认为这比使用ObjectUtils.defaultIfNull做同样的事情要好一些。 但是,有一个警告。 您不得使用Optional.orElse()来调用具有副作用的方法。 例如,在我的社交网络练习的其他地方,我有代码来搜索用户,并在找到用户后将其返回,否则它将创建一个新用户:

Optional<User> userOpt = userRepository.find(recipient);
if (userOpt.isPresent()) {return userOpt.get();
}
return createUser();

您可能会假设您可以像下面这样重写此代码:

return userRepository.find(recipient).orElse(createUser());

您不必这样做,因为无论是否存在可选参数,都会始终调用createUser() ! 几乎可以肯定这不是您想要的:充其量您将进行不必要的方法调用,并且,如果该方法有副作用,则可能会引入错误。 相反,您应该调用Optional.orElseGet()并为它提供一个提供默认值的Supplier

return userRepository.find(recipient).orElseGet(() -> createUser());

现在,仅当不存在可选参数时才调用createUser() ,这是我想要的行为。 再一次,我们可以将lambda替换为方法参考:

return userRepository.find(recipient).orElseGet(this::createUser);

抛出异常

对于您来说,当可选项不存在并且您想引发异常时,可能是错误情况。 您可以通过调用Optional.orElseThrow()并将其传递给创建异常的Supplier来实现:

return userRepository.find(recipient).orElseThrow(() -> new RuntimeException("User " + recipient + " not found"));

映射可选值

Optional还有一些方法,使您可以执行类似于流上的操作。 例如,在另一个练习中,我有一些结构类似于此的代码:

Optional<Amount> creditAmountOpt = transaction.getCreditAmount();
Optional<Amount> debitAmountOpt = transaction.getDebitAmount();String formattedDepositAmount = creditAmountOpt.isPresent() ?formatAmount(creditAmountOpt.get()) : " ";String formattedWithdrawalAmount = debitAmountOpt.isPresent() ?formatAmount(debitAmountOpt.get()) : " ";return String.format(" %s| %s|", formattedDepositAmount, formattedWithdrawalAmount);

该代码的上下文是一个打印银行对帐单行的类:我的Transaction类知道这是存款还是取款,但我不希望对帐单行打印机知道。 因此,我让Transaction接口返回了借方和贷方金额的可选值:语句行打印机将对每个值(如果存在)进行格式化,如果不存在则替换为空格。

为了避免条件运算符,我们可以使用Optional.map()方法。 这与Stream API上的map方法非常相似。 它接受一个Function并在存在可选选项时调用它。 它将包装的值作为函数参数传递,并将返回值包装在另一个Optional中。 因此,在这种情况下,它将Optional<Amount>映射到Optional<String> 。 这使我们可以像这样重写代码:

return String.format(" %s| %s|",transaction.getDepositAmount().map(this::formatAmount).orElse(" "),transaction.getWithdrawalAmount().map(this::formatAmount).orElse(" "));

您可能想知道,如果映射一个返回另一个可选参数的函数,即Function<T, Optional<U>> ,在这种情况下,您最终得到的是Optional<Optional<U>>类型的结果,这可能不是你要。 同样,类似于流,您可以改用flatMap()返回一个Optional<U>值。

与流的相似性扩展到Optional.filter() ,它评估提供的谓词(如果存在可选值),并且当谓词评估为false时,它将返回一个空的可选。 明智的是,避免变得过于可爱,如果不加注意,您可能会得到难以理解的代码。 可选选项最适合用于重构简单明了的代码,但是将其简化为简单明了的代码。

但小心点

最后,任何有用的工具都可能被滥用,因此Optional也是如此。 它们仅用于表示返回值。 如果您声明类型为Optional的实例变量,IntelliJ将向您发出警告。 这构成了一个临时字段的显式声明,该字段被视为代码气味 。 另外,不要将Optionals用作方法参数:本质上,这是伪装的布尔型参数,也被认为很臭。 如果发现自己想这样做,最好将方法分为两个方法:一个带有参数,另一个不带参数,然后将条件放在客户端代码中。

翻译自: https://www.javacodegeeks.com/2017/11/java-optionals-expressive-code.html

optionals

optionals_Java Optionals获得更具表现力的代码相关推荐

  1. Java Optionals获得更具表现力的代码

    我们中任何人使用允许空引用的语言进行编程时,都会遇到尝试取消引用一个引用时发生的情况. 无论是导致segfault还是NullPointerException,它始终是一个错误. 托尼·霍尔将其描述为 ...

  2. specs.4.8.gz_使用Specs2和客户端API 2.0进行富有表现力的JAX-RS集成测试

    specs.4.8.gz 毫无疑问, JAX-RS是一项杰出的技术. 即将发布的规范JAX-RS 2.0带来了更多的强大功能,尤其是在客户端API方面. 今天的帖子的主题是JAX-RS服务的集成测试. ...

  3. 使用Specs2和客户端API 2.0进行富有表现力的JAX-RS集成测试

    毫无疑问, JAX-RS是一项杰出的技术. 即将发布的规范JAX-RS 2.0带来了更多的强大功能,尤其是在客户端API方面. 今天的帖子的主题是JAX-RS服务的集成测试. 有很多出色的测试框架,例 ...

  4. 战斗 表现和逻辑分离_冗长的优点和表现力的缺点

    战斗 表现和逻辑分离 Java太冗长了! 谁没有以前在互联网上偶然发现过这样的声音? 那个吹嘘[在那儿插入表达语言]的人很快就取代了Java,因为它更加简洁:它可以用一行代码代替那10行Java代码. ...

  5. 如何制作一份更具洞察力的商业BI报告?

    随着市场环境的复杂化,在数据分析中,能否提供更具商业洞察力的数据信息正在成为考核业务员能力的重要参考指标.加强以下两大块能力至关重要: 1.业务相关专业能力以及相关知识 2.对工具的驾驭能力,大部分人 ...

  6. JavaScript设计模式—富有表现力的Javascript(一)

    学习引言: javascript设计模式是图灵出版,学习中力求每个章节都细看.   正题:      1.1 javascript的灵活性 面向对象对象的Javascript编程模式:1.可以保存状态 ...

  7. 用阿里云托管服务器怎么托管_云托管使企业更具竞争力的8个原因

    用阿里云托管服务器怎么托管 Organisations are flocking to cloud computing in greater numbers than ever before and ...

  8. [未来成长] 分享:《麦肯锡教我的写作武器》如何写出一篇具有逻辑表现力的文案...

    作为职场人,写文案.做报告是一定缺不了的.但你是不是经常遇到这样的情况:把 PPT 的标题写上,脑袋就一片空白了?通宵达旦整出一篇数据详实.案例充足的报告,上司却说你没观点.没重点:汇报刚讲5页,就感 ...

  9. 冗长的优点和表现力的缺点

    Java太冗长了! 谁没有以前在互联网上偶然发现过这样的声音? 那个吹嘘[在那儿插入表达语言]的人很快就取代了Java,因为它更加简洁:它可以用一行代码代替那10行Java代码. 啊,力量! 不幸的是 ...

最新文章

  1. 话说文件系统——aufs源码分析(三)【转】
  2. 1.8 Boolean类
  3. sklearn自学指南(part35)--近邻传播及均值漂移
  4. python修炼第四天
  5. lua工具库penlight--08额外的库(二)
  6. junit:junit_简而言之,JUnit:Hello World
  7. 41.进程池--Pool
  8. python怎么通过占位符_如何输入占位符?
  9. 我们的产品工艺演示动画
  10. 卸载python2.7_Office卸载工具官方版
  11. EEMD算法原理及应用
  12. 2020年四季度混合型基金数据分析
  13. 有关php外文期刊,口腔外文杂志、收录数据库、参考信息汇总
  14. 谷歌seo外链发布50+个网站平台分享(e6zzseo)
  15. Docker基础之containerd的shim
  16. 计算机考研山西大学和太原理工大学,山西大学和太原理工大学,山西省内的两所知名高校,哪所更强?...
  17. 名帖292 张瑞图 行书《论书卷》
  18. Layui(四) 表格嵌套下拉列表框
  19. S32K144库函数
  20. Java程序员面试题集(131-135)

热门文章

  1. CodeForces - 1189B Number Circle
  2. 模板:后缀数组(SA)
  3. YBTOJ:圈套问题(分治法、鸽笼原理)
  4. CF464E-The Classic Problem【最短路,主席树】
  5. P3705-[SDOI2017]新生舞会【0/1分数规划,费用流】
  6. 牛客-牛牛的猜球游戏
  7. jzoj3086,luogu3831-[SHOI2012]回家的路【最短路,拆点】
  8. Codeforces Round #673 (Div. 2)——待补 E
  9. 【动态规划】方格取数 (ssl 1010)
  10. SpringCloud Zuul(三)之常见用法