大约两周前,Stephen Colebourne提出了使用Optional的实用方法 。 如果您阅读了它,您可能会从我以前的建议中猜到我不同意。

总览

我必须以免责声明开头,但随后我将直接解释为什么我认为他的方法不够理想。

所有不归因于他人的报价均摘自Stephen的帖子。 虽然并非绝对必要,但我建议先阅读它。 但是别忘了回来!

我创建了三个要点,这些要点在整个帖子中都会介绍: Stephen版本 , 基本版本和扩展版本中的相同示例。

免责声明

Stephen Colebourne是Java的传奇人物。 引用Markus Eisele的Java英雄关于他的文章:

Stephen Colebourne是OpenGamma的技术人员。 他以其在开源和博客中的工作而闻名。 他创建了Joda-Time,现在将其进一步开发为JSR-310 / ThreeTen。 他为关于Java未来的辩论做出了贡献,包括为泛型和FCM闭包的钻石运算符提出的建议,两者都接近于Java 7和8中已采用的更改。Stephen是会议的常任发言人,JavaOne Rock Star和Java Champion。 。

我很高兴为斯蒂芬的房地产联盟做出贡献,这增强了我对他作为一个非常有能力的开发商和一个非常有思想的人的看法。

所有这些都表明,如果有疑问,请相信他。

事实是,他的方法根植于公理,即Optional应该仅用作返回类型。 这完全符合那些首先介绍该课程的人的建议。 引用Brian Goetz的话 :

当然,人们会做他们想要的。 但是,添加此功能时我们确实有明确的意图,并且它并不是通用的Maybe或Some类型,这是许多人希望我们这样做的原因。 我们的意图是为库方法返回类型提供一种有限的机制,其中需要一种明确的方式来表示“无结果”,而为此使用null则极有可能导致错误。

[…]几乎永远不要将其用作某些内容或方法参数的字段。

因此,如果有疑问,请相信他对我的看法。

发布时间由JD汉考克在CC-BY 2.0 。

并置

当然,比只相信任何人更好的是下定决心。 因此,与斯蒂芬的观点相反,这是我的观点。

基本要点

这些是斯蒂芬的五个基本要点:

  1. 不要声明任何类型为Optional的实例变量。
  2. 使用null表示类的私有范围内的可选数据。
  3. 对于访问可选字段的吸气剂,请使用可选。
  4. 不要在setter或构造方法中使用Optional。
  5. 对于具有可选结果的任何其他业务逻辑方法,请使用Optional作为返回类型。

这是我的:

  1. 设计代码,以尽可能避免可选性。
  2. 在所有其他情况下,请选择Optional而不是null。

例子

让我们比较例子。 他的是:

Address.java,作者:Stephen Colebourne

public class Address {private final String addressLine;  // never nullprivate final String city;         // never nullprivate final String postcode;     // optional, thus may be null// constructor ensures non-null fields really are non-null// optional field can just be stored directly, as null means optionalpublic Address(String addressLine, String city, String postcode) {this.addressLine = Preconditions.chckNotNull(addressLine);this.city = Preconditions.chckNotNull(city);this.postcode = postcode;}// normal getterspublic String getAddressLine() {return addressLine;}public String getCity() {return city;}// special getter for optional fieldpublic Optional<String> getPostcode() {return Optional.ofNullable(postcode);}// return optional instead of null for business logic methods that may not find a resultpublic static Optional<Address> findAddress(String userInput) {return... // find the address, returning Optional.empty() if not found}}

我喜欢此类的任何使用者都不能接收null。 我不喜欢您仍然需要如何处理-在课堂上还是在课堂上。

这将是我的(基本)版本:

我的Address.java(基本版)

public class Address {// look ma, no comments requiredprivate final String addressLine;private final String city;private final Optional<String> postcode;// nobody has to look at this constructor to check which parameters are// allowed to be null because of course none are!public Address(String addressLine, String city, Optional<String> postcode) {this.addressLine = requireNonNull(addressLine,"The argument 'addressLine' must not be null.");this.city = requireNonNull(city,"The argument 'city' must not be null.");this.postcode = requireNonNull(postcode,"The argument 'postcode' must not be null.");}// of course methods that might not have a result// return 'Optional' instead of nullpublic static Optional<Address> findAddress(String userInput) {// find the address, returning Optional.empty() if not found}// getters are straight forward and can be generatedpublic String getAddressLine() {return addressLine;}public String getCity() {return city;}// look how the field's type matches the getter's type;// nice for bean-based code/toolspublic Optional<String> getPostcode() {return postcode;}}

这里根本没有空值。

差异性

约束问题

在对象内,开发人员仍然被迫考虑null并使用!= null检查对其进行管理。 这是合理的,因为null问题受到了限制。 代码将全部作为一个单元编写和测试(您确实编写测试不是吗?),因此null不会引起很多问题。

您看到他的构造函数如何允许其中一个参数为null吗? 找出哪一个需要您离开正在做的事情并查看其他类的代码的唯一方法。 这不是什么大事,但是没有必要。

即使撇开这一点,问题也没有受到应有的限制。 假设每个人都不喜欢注释 ,我们必须假设它们不存在,这使构造函数内部和getter的返回类型告诉您该字段可为空。 这不是让您跳出来的最佳信息。

很明显可选很明显

public class Address {// look ma, no comments requiredprivate final String addressLine;private final String city;private Optional<String> postcode;// nobody has to look at these constructors to check which parameters are// allowed to be null because of course none are!public Address(String addressLine, String city, Optional<String> postcode) {this.addressLine = requireNonNull(addressLine,"The argument 'addressLine' must not be null.");this.city = requireNonNull(city,"The argument 'city' must not be null.");this.postcode = requireNonNull(postcode,"The argument 'postcode' must not be null.");}public Address(String addressLine, String city, String postcode) {// use 'requireNonNull' inside Optional factory method// if you prefer a verbose exception message;// otherwise 'Optional.of(postcode)' sufficesthis(addressLine, city, Optional.of(requireNonNull(postcode,"The argument 'postcode' must not be null.")));}public Address(String addressLine, String city) {this(addressLine, city, Optional.empty());}// now if some method needs to use the postcode,// we can not overlook the fact that it is optionalpublic int comparePostcode(Address other) {// without Optionals we might overlook that the postcode// could be missing and do this:// return this.postcode.compareTo(other.postcode);if (this.postcode.isPresent() && other.postcode.isPresent())return this.postcode.get().compareTo(other.postcode.get());else if (this.postcode.isPresent())return 1;else if (other.postcode.isPresent())return -1;elsereturn 0;}// of course methods that might not have a result// return 'Optional' instead of nullpublic static Optional<Address> findAddress(String userInput) {// find the address, returning Optional.empty() if not found}// getters are straight forward and can be generatedpublic String getAddressLine() {return addressLine;}public String getCity() {return city;}// look how the field's type matches the getter's type;// nice for bean-based code/toolspublic Optional<String> getPostcode() {return postcode;}// in case this 'Address' is mutable// (which it probably shouldn't be but let's presume it is)// you can decide whether you prefer a setter that takes an 'Optional',// a pair of methods to set an existing and an empty postcode, or bothpublic void setPostcode(Optional<String> postcode) {this.postcode = requireNonNull(postcode,"The argument 'postcode' must not be null.");}public void setPostcode(String postcode) {// again you might want to use 'requireNonNull'// if you prefer a verbose exception message;this.postcode = Optional.of(requireNonNull(postcode,"The argument 'postcode' must not be null."));}public void setEmptyPostcode() {this.postcode = Optional.empty();}}

他的测试论据可能会被数字打断。 如果所有测试都包含所有字段,则每个可选字段将使测试数量加倍,因为对于空和非空情况都应运行每个测试。 我更喜欢将类型系统作为第一道防线。

另一方面,这种痛苦可能会说服开发人员在单个类中找到具有较少可选项的解决方案。

性能

Stephen正确指出,为方法返回值创建的实例然后被快速丢弃(这对于Optional的使用是典型的),几乎没有成本。 与Optional字段不同,后者在整个包含对象的整个生命周期中都存在,并增加了从该对象到Optional的有效负载的间接附加层。

对他来说,这是更喜欢null的原因。

虽然很容易断言这是“过早的优化”,但作为工程师,我们有责任了解所使用系统的限制和功能,并仔细选择应强调的点。

我同意。 但是对我来说,谨慎选择的一部分意味着要首先进行概要介绍。 而且,如果有人向我展示令人信服的论点,即在他的具体情况下,将某些Optional字段替换为可为空的字段会导致明显的性能提升,那么我会立即删除它们的愚蠢框。 但是在所有其他情况下,我坚持使用我认为更易于维护的代码。

顺便说一句,可以为使用数组而不是ArrayLists或使用char-arrays而不是字符串提供相同的参数。 我敢肯定,没有明显的性能提升,没有人会遵循该建议。

但是,讨论中的该重复主题值得关注。 我将尝试寻找一些时间来介绍一些我认为很有趣的用例。

可序列化

尽管这只是次要点,但应注意,该类可以是可序列化的,如果任何字段是Optional的,则这是不可能的(因为Optional不实现Serializable)。

我认为这是可以解决的 。 但是,这会导致一些额外的工作。

方便

我的经验是,在设置程序或构造函数上使用Optional对调用者很烦,因为它们通常具有实际的对象。 强制调用者将参数包装在Optional中是一种麻烦,我希望不要对用户造成影响。 (即便利性胜过输入的严格性)

虽然编写令人讨厌的代码可能很有趣,但我明白了他的观点。 所以不要强迫用户, 重载您的方法 :

重载构造函数以避免创建可选项

public class Address {// look ma, no comments requiredprivate final String addressLine;private final String city;private Optional<String> postcode;// nobody has to look at these constructors to check which parameters are// allowed to be null because of course none are!public Address(String addressLine, String city, Optional<String> postcode) {this.addressLine = requireNonNull(addressLine,"The argument 'addressLine' must not be null.");this.city = requireNonNull(city,"The argument 'city' must not be null.");this.postcode = requireNonNull(postcode,"The argument 'postcode' must not be null.");}public Address(String addressLine, String city, String postcode) {// use 'requireNonNull' inside Optional factory method// if you prefer a verbose exception message;// otherwise 'Optional.of(postcode)' sufficesthis(addressLine, city, Optional.of(requireNonNull(postcode,"The argument 'postcode' must not be null.")));}public Address(String addressLine, String city) {this(addressLine, city, Optional.empty());}// now if some method needs to use the postcode,// we can not overlook the fact that it is optionalpublic int comparePostcode(Address other) {// without Optionals we might overlook that the postcode// could be missing and do this:// return this.postcode.compareTo(other.postcode);if (this.postcode.isPresent() && other.postcode.isPresent())return this.postcode.get().compareTo(other.postcode.get());else if (this.postcode.isPresent())return 1;else if (other.postcode.isPresent())return -1;elsereturn 0;}// of course methods that might not have a result// return 'Optional' instead of nullpublic static Optional<Address> findAddress(String userInput) {// find the address, returning Optional.empty() if not found}// getters are straight forward and can be generatedpublic String getAddressLine() {return addressLine;}public String getCity() {return city;}// look how the field's type matches the getter's type;// nice for bean-based code/toolspublic Optional<String> getPostcode() {return postcode;}// in case this 'Address' is mutable// (which it probably shouldn't be but let's presume it is)// you can decide whether you prefer a setter that takes an 'Optional',// a pair of methods to set an existing and an empty postcode, or bothpublic void setPostcode(Optional<String> postcode) {this.postcode = requireNonNull(postcode,"The argument 'postcode' must not be null.");}public void setPostcode(String postcode) {// again you might want to use 'requireNonNull'// if you prefer a verbose exception message;this.postcode = Optional.of(requireNonNull(postcode,"The argument 'postcode' must not be null."));}public void setEmptyPostcode() {this.postcode = Optional.empty();}}

当然,这在许多可选字段中无法很好地扩展。 在这种情况下,构建器模式会有所帮助。

事实是,如果我们的可为空的邮政编码中有一个setter,则处理其他代码的开发人员必须再次停止并查看此类以确定她是否可以传递null。 而且由于她永远不能确定,因此她也必须检查其他吸气剂。 谈论烦人的代码...

使用Optional类型的字段,setter可能如下所示:

重载的二传手,避免创建可选项

public class Address {// look ma, no comments requiredprivate final String addressLine;private final String city;private Optional<String> postcode;// nobody has to look at these constructors to check which parameters are// allowed to be null because of course none are!public Address(String addressLine, String city, Optional<String> postcode) {this.addressLine = requireNonNull(addressLine,"The argument 'addressLine' must not be null.");this.city = requireNonNull(city,"The argument 'city' must not be null.");this.postcode = requireNonNull(postcode,"The argument 'postcode' must not be null.");}public Address(String addressLine, String city, String postcode) {// use 'requireNonNull' inside Optional factory method// if you prefer a verbose exception message;// otherwise 'Optional.of(postcode)' sufficesthis(addressLine, city, Optional.of(requireNonNull(postcode,"The argument 'postcode' must not be null.")));}public Address(String addressLine, String city) {this(addressLine, city, Optional.empty());}// now if some method needs to use the postcode,// we can not overlook the fact that it is optionalpublic int comparePostcode(Address other) {// without Optionals we might overlook that the postcode// could be missing and do this:// return this.postcode.compareTo(other.postcode);if (this.postcode.isPresent() && other.postcode.isPresent())return this.postcode.get().compareTo(other.postcode.get());else if (this.postcode.isPresent())return 1;else if (other.postcode.isPresent())return -1;elsereturn 0;}// of course methods that might not have a result// return 'Optional' instead of nullpublic static Optional<Address> findAddress(String userInput) {// find the address, returning Optional.empty() if not found}// getters are straight forward and can be generatedpublic String getAddressLine() {return addressLine;}public String getCity() {return city;}// look how the field's type matches the getter's type;// nice for bean-based code/toolspublic Optional<String> getPostcode() {return postcode;}// in case this 'Address' is mutable// (which it probably shouldn't be but let's presume it is)// you can decide whether you prefer a setter that takes an 'Optional',// a pair of methods to set an existing and an empty postcode, or bothpublic void setPostcode(Optional<String> postcode) {this.postcode = requireNonNull(postcode,"The argument 'postcode' must not be null.");}public void setPostcode(String postcode) {// again you might want to use 'requireNonNull'// if you prefer a verbose exception message;this.postcode = Optional.of(requireNonNull(postcode,"The argument 'postcode' must not be null."));}public void setEmptyPostcode() {this.postcode = Optional.empty();}}

同样,所有空值都将立即被例外回答。

豆子

不利的一面是,这种方法导致的对象不是bean。

是的 具有Optional类型的字段不会因此受到影响。

共同点

我们在这里讨论细节不容忽视。 我们的目标是相同的,并且我们提出了类似的实现目标的方法。

如果在应用程序中广泛使用,则null问题趋于消失而无需付出很大的努力。 由于每个域对象都拒绝返回null,因此应用程序往往永远不会传递null。 以我的经验,采用这种方法往往会导致在类的私有范围之外从未使用过null的代码。 重要的是,这自然而然地发生了,而不是一个痛苦的过渡。 随着时间的流逝,您开始编写防御性较低的代码,因为您更有信心没有任何变量实际包含null。

这是一个伟大的目标! 并遵循斯蒂芬的建议将带给您大部分帮助。 因此,不要以我的不同意为理由,至少不使用Optional。

我要说的是,我几乎没有理由停止更多地禁止null!

反射

每当有可为空的内容时,我就解决一些问题并希望驳斥一些反对使用Optional的论点。 我希望表明我更严格的方法在驱散null方面做得更好。 这应该使您有更多的精力去思考更多相关的问题。

付出的代价可能会降低性能。 如果有人证明更多,对于这些特定情况,我们仍然可以返回null。 或将硬件扔在问题上。 或等待值类型 。

你怎么看?

翻译自: https://www.javacodegeeks.com/2015/08/java-8-se-optional-a-strict-approach.html

Java 8 SE可选,严格的方法相关推荐

  1. wsl 或者window terminal 下出现Java 14 switch expressions unrecognized解决方法:升级java jdk到14 or later

    文章目录 缘由 解决方法 参考 缘由 IDEA中build成功,但是automated build失败 $ ./gradlew> Task :compileJava FAILED Wordle/ ...

  2. java程序设计_JAVA基础程序设计之方法

    1 基本概念 Java 方法是语句的集合,它们在一起执行一个功能. l 方法是解决一类问题的步骤的有序组合 l 方法包含于类或对象中 l 方法在程序中被创建,在其他地方被引用 1.1 方法的定义 一般 ...

  3. JAVA中dot的用法_Java 方法

    方法(有的人喜欢叫函数)是一段可重用的代码段. 一.方法的使用 1. 方法定义 方法定义的语法形式: [修饰符] 返回值类型 方法名([参数类型 参数名]){ ... 方法体 ...return返回值 ...

  4. java 发送短信 多通道_一种Java卡多通道临时对象管理方法与流程

    本发明涉及Java智能卡领域,具体涉及一种Java卡多通道临时对象管理方法. 背景技术: :JavaCard规范支持逻辑通道的概念,允许最多智能卡中的16个应用程序会话同时开启,每个逻辑通道一个会话. ...

  5. Java String API 常用的String方法详解

    标题 String类的特性 Java中String类的构造方法 String类的 intern() 注意还跟jdk有关 如何保证变量S指向的是字符串常量池中的数据呢? 关于String中 new St ...

  6. Java—重复调用的代码块—方法

    对 java 知识点重新进行了总结与更新,点我查看 在面向对象的程序设计中,方法是一个很重要的概念,体现了面向对象三大要素中"封装"的思想."方法"又被称为&q ...

  7. java set 包含_Java Set.contains()方法:判断Set集合是否包含指定的对象

    Java 集合类中的 Set.contains() 方法判断 Set 集合是否包含指定的对象.该方法返回值为 boolean 类型,如果 Set 集合包含指定的对象,则返回 true,否则返回 fal ...

  8. Java培训教程:”==“和 equals 方法究竟有什么区别?

    在学习java技术过程中,我们会接触到一些变量值的相关知识,本期小编为大家介绍的教程就是关于"=="和 equals 方法究竟有什么区别?来看看下面的详细介绍. Java培训教程: ...

  9. Java 查看文件绝对路径,JAVA获取文件绝对路径的方法

    本文实例讲述了JAVA获取文件绝对路径的方法.分享给大家供大家参考.具体实现方法如下: /** * 获取一个类的class文件所在的绝对路径. 这个类可以是JDK自身的类,也可以是用户自定义的类,或者 ...

最新文章

  1. 生成公钥链接github
  2. QOS的qmtoken 1
  3. 日期、时间库使用备注
  4. Power BI for Office 365(七) Power BI站点
  5. LeetCode Maximum Depth of Binary Tree
  6. 搭建JMeter+Jenkins+Ant持续化
  7. 极速pdf编辑器的水印如何去掉_如何修改PDF?有没有详细的PDF编辑器操作方法?...
  8. ZooKeeper(二)ZooKeeper能做什么?
  9. CentOS7挂载nfs盘快速指南
  10. C#使用模板文件批量导出word文档
  11. 安装vsphere update manager及注意事项
  12. java 观察者模式_观察者模式(Observer Pattern)
  13. 医疗服务机器人市场复合年增长率将达15.7%
  14. October CMS - 快速入门 2 基本概念
  15. python数据分析实战 fabio nelli百度云_Python数据分析实战 内利(Fabio Nelli),杜春晓 9787115432209...
  16. Android图片背景朦胧效果,android 图片的模糊化处理,效果类似超级课程表的“我的中心里头像背景,看起来很炫...
  17. Win7如何显示/隐藏Administrator账号
  18. 中兴软件笔试 c语言,中兴通讯软件工程师面试经验
  19. IOS 发布被拒 PLA 1.2问题 整个过程介绍 03 个人账户升级公司账户
  20. 解决龙格现象matlab,matlab实现Lagrange多项式插值观察龙格现象

热门文章

  1. jquery给轮播图的第一张设置class样式
  2. 使用加密工具类进行有效的字符串加密——CSDN博客
  3. org.springframework.amqp.AmqpConnectException java.net.ConnectException的解决办法
  4. idea中Gitlab项目导入导出
  5. =空值返回空值_@ParameterizedTest在@CvsSource中具有空值
  6. java github_GitHub Research:超过50%的Java记录语句写错了
  7. javafx 内存占用_JavaFX:TouchGesture内存泄漏?
  8. jboss4 迁移_应用程序服务器迁移:从JBoss EE5到Wildfly EE7
  9. java ee jsp_EE JSP:使用自定义标签库生成动态内容
  10. 章鱼扫描仪:Java构建工具和恶意软件