Optional
类型对于许多开发人员来说是一个新事物。
getter方法返回Optional<Foo>
类型代替经典Foo
是一种好习惯吗?假设值可以是null
。
答案 0 :(得分:418)
当然,人们会做他们想做的事。但是在添加此功能时我们确实有一个明确的意图,并且不是一个通用的类型,就像许多人希望我们这样做一样。我们的目的是为库方法返回类型提供一种有限的机制,其中需要一种明确的方式来表示&#34;没有结果&#34;,并且使用null
这样的绝对可能导致错误。
例如,您可能永远不应该将它用于返回结果数组或结果列表的内容;而是返回一个空数组或列表。您几乎不应该将它用作某事物或方法参数的字段。
我认为经常使用它作为吸气剂的返回值肯定会过度使用。
过度使用。
(公共服务公告:从不致电Optional.get
,除非您能证明它永远不会为空;而是使用其中一种安全方法,例如orElse
或{{1}回想起来,我们应该调用类似ifPresent
之类的get
或更清楚的东西,这是一个非常危险的方法,它首先破坏了getOrElseThrowNoSuchElementException
的整个目的。经验教训。(更新:Java 10有Optional
,在语义上等同于Optional.orElseThrow()
,但其名称更合适。)
答案 1 :(得分:67)
在对自己进行了一些研究之后,我发现了一些可能暗示适当的事情。最具权威性的是来自Oracle文章的以下引用:
&#34;重要的是要注意Optional类的意图是不要替换每个空引用。相反,它的目的是帮助设计更易于理解的API ,这样只需读取方法的签名,就可以判断是否可以期望一个可选值。这会强制您主动打开Optional来处理缺少值。&#34; - Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional!
我还从Java 8 Optional: How to use it
中找到了这段摘录&#34;可选不适合在这些环境中使用,因为它不会给我们任何东西:
域模型层中的
- (不可序列化)
DTO中的- (同样的原因)
- 方法的输入参数
- 在构造函数参数&#34;
中
这也似乎提出了一些有效的观点。
我无法找到任何负面含义或危险信号,表明应该避免使用Optional
。我认为一般的想法是,如果它有用或者提高了API的可用性,请使用它。
答案 2 :(得分:16)
我总体上说,将可选类型用于可以为空的返回值是一个好主意。但是,w.r.t。到框架我假设用可选类型替换经典getter在使用依赖于getter和setter的编码约定的框架(例如,Hibernate)时会带来很多麻烦。
答案 3 :(得分:9)
Optional
被添加到Java的原因是:
return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.findFirst()
.getOrThrow(() -> new InternalError(...));
比这还干净:
Method matching =
Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.getFirst();
if (matching == null)
throw new InternalError("Enclosing method not found");
return matching;
我的意思是, Optional是为支持功能性编程而编写的,它是同时添加到Java中的。 (该示例由blog by Brian Goetz提供。一个更好的示例可能使用orElse()
方法,因为此代码无论如何都会引发异常,但您可以理解。)
但是现在,人们使用Optional的原因非常不同。他们正在使用它来解决语言设计中的缺陷。缺陷在于:无法指定API的哪个参数和返回值允许为空。它可能会在javadocs中提到,但是大多数开发人员甚至都没有为他们的代码编写javadocs,并且很少有人会在编写时检查javadocs。因此,这导致很多代码在使用空值之前总是检查空值,即使它们经常不可能为空值也是如此,因为它们已经在调用堆栈中重复验证了九到十次。
我认为真正需要解决此缺陷,因为看到新Optional类的许多人都认为它的目的是增加API的清晰度。这就是为什么人们会问诸如“吸气剂应返回可选件”之类的问题吗?不,他们可能不应该这样做,除非您希望将getter用于函数式编程中,否则这种可能性很小。实际上,如果您查看Optional在Java API中的使用位置,那么它主要在Stream类中,Stream类是函数式编程的核心。 (我还没有进行彻底检查,但是Stream类可能是使用它们的 only 位置。)
如果您确实打算在一些功能代码中使用吸气剂,那么最好有一个标准的吸气剂,然后再准备一个返回Optional的吸气剂。
哦,如果您需要类可序列化,则绝对不要使用Optional。
对于API缺陷,可选方案是非常糟糕的解决方案,因为a)非常冗长,并且b)从来没有打算首先解决该问题。
Nullness Checker是API漏洞更好的解决方案。这是一个注释处理器,通过使用@Nullable对其进行注释,您可以指定允许哪些参数和返回值为空。这样,编译器可以扫描代码并确定是否将实际上可以为null的值传递给不允许null的值。默认情况下,它假定除非有注释,否则什么都不能为null。这样,您不必担心空值。将空值传递给参数将导致编译器错误。测试对象的null不能为null会产生编译器警告。这样做的结果是将NullPointerException从运行时错误更改为编译时错误。
这会改变一切。
对于您的吸气剂,请勿使用Optional。并尝试设计您的类,以便所有成员都不能为空。也许尝试将Nullness Checker添加到项目中,并在需要时声明@Nullable的getter和setter参数。我只在新项目中做到了这一点。在现有的项目中可能会产生很多警告,这些警告中编写了许多多余的null测试,因此可能很难进行改进。但这也会捕获很多错误。我喜欢它。因此,我的代码更加简洁,可靠。
(还有一种新的语言可以解决这个问题。Kotlin可以编译为Java字节码,它允许您在声明对象时指定对象是否可以为null。这是一种更干净的方法。)
原始帖子的附录(版本2)
经过深思熟虑,我很不情愿地得出一个结论,在一个条件下返回Optional是可以接受的:检索到的值实际上可能为null。我看过很多代码,人们通常从无法正常返回null的getter中返回Optional。我认为这是一种非常糟糕的编码实践,只会增加代码的复杂性,从而更容易出现错误。但是,当返回的值实际上可能为null时,请继续将其包装在Optional中。
请记住,为函数式编程设计且需要函数引用的方法将(并且应该)以两种形式编写,其中一种使用Optional。例如,Optional.map()
和Optional.flatMap()
都采用函数引用。第一个引用了一个普通的getter,第二个引用了一个返回Optional的引用。因此,如果返回值不能为null的Optional,就不会帮任何忙。
话虽如此,我仍然看到Nullness Checker使用的方法是处理null的最佳方法,因为它们将NullPointerExceptions从运行时错误转换为编译时错误。
答案 4 :(得分:3)
如果您正在使用现代序列化程序和其他理解Optional
的框架,那么我发现这些指南在编写Entity
bean时运行良好域图层:
null
中列BAR
中的单元格的FOO
值,则getter Foo.getBar()
可以返回{{ 1}}向开发人员指出该值可能合理地预期为null并且他们应该处理这个。如果数据库保证该值不为null,那么getter应该不将其包装在Optional
中。Optional
应为Foo.bar
且不为private
。如果是Optional
,那么Optional
真的没有理由。private
应采用Foo.setBar(String bar)
和不 bar
的类型。如果可以使用Optional
参数,则在JavaDoc注释中说明。如果不能使用null
null
或某些适当的业务逻辑,恕我直言,更合适。 IllegalArgumentException
个参数(原因类似于第3点)。通常我只在构造函数中包含必须在序列化数据库中为非null的参数。为了提高上述效率,您可能需要编辑IDE模板以生成Optional
,toString()
等的getter和相应模板,或者直接使用字段(大多数IDE生成器已经处理) with nulls)。