Optional
类型对于许多开发人员来说是新事物。吸气方法是否返回
Optional<Foo>
类型代替经典的Foo
是一种好习惯?假设该值可以是null
。#1 楼
当然,人们会做他们想要的。但是,添加此功能时我们确实有明确的意图,并且它并不是通用的Maybe类型,因为许多人希望我们这样做。我们的意图是为库方法返回类型提供一种有限的机制,该机制需要一种明确的方法来表示“无结果”,而为此使用null
极有可能引起错误。 例如,您可能永远不要将其用于返回结果数组或结果列表的东西;而是返回一个空数组或列表。您几乎应该永远不要将其用作某些内容或方法参数的字段。
我认为,常规地将其用作getter的返回值肯定会过度使用。
应该避免使用Optional并没有什么问题,这不是很多人希望的那样,因此我们非常担心过度使用的风险。
(公共服务公告:除非可以证明它永远不会为null,否则请不要调用
Optional.get
;而应使用诸如orElse
或ifPresent
之类的安全方法之一。回想起来,我们应该将get
称为getOrElseThrowNoSuchElementException
之类,或更清晰的名称这是一个非常危险的方法,首先破坏了Optional
的整个目的(经验教训。)(更新:Java 10具有Optional.orElseThrow()
,从语义上等效于get()
,但其名称更合适。))评论
(关于最后一部分)…并且当我们确信该值永远不会为null时,我们可以使用orElseThrow(AssertionError :: new),hem或orElseThrow(NullPointerException :: new)…
–霍尔格
2014年10月13日在18:37
如果您打算引入通用的Maybe或Some类型,您会做些什么不同?有没有一种方法使Optional不适合某个人,还是只是在新API上引入Optionals会使它成为非Java风格的?
– David Moles
15年2月13日在17:15
我所说的“通用类型”是指将其构建到语言的类型系统中,而不是提供一个近似它的库类。 (某些语言具有T?(T或null)和T!(不可空T)的类型)。我们不能像语言支持那样在Foo和Optional
–布莱恩·格茨(Brian Goetz)
15年2月13日在21:26
我想知道为什么Java世界中的重点是Optional,而不是更好的静态分析。 Optional确实有一些优势,但是null具有的巨大优势是向后兼容性; Map :: get返回一个可为空的V,而不是Optional
– yshavit
2015年4月18日在16:22
我不知道使用它作为属性的返回值并不是所有人都想要的,除非我在StackOverflow上阅读了此答案。实际上,我被认为是您在Oracle网站上阅读本文后的全部意图:oracle.com/technetwork/articles/java/…谢谢您的澄清
–詹森·汤普森(Jason Thompson)
15年5月25日在22:06
#2 楼
在进行了一些我自己的研究之后,我遇到了许多可能建议何时合适的事情。最权威的是Oracle文章中的以下引文:请注意,Optional类的目的不是替换每个单个null引用,而是其目的,这一点很重要。是为了帮助设计更容易理解的API,以便仅通过读取方法的签名就可以知道是否可以期望一个可选值。这迫使您主动展开一个Optional以应对缺少值的情况。” -厌倦了空指针异常?考虑使用Java SE 8的可选功能!
我也从Java 8可选内容中摘录了此内容:如何使用它
“可选功能并不意味着可以在这些情况下使用,因为它不会给我们带来任何好处:
DTO中的域模型层(不可序列化)
(相同的原因)
在方法的输入参数中
在构造函数参数中“
这似乎也提出了一些有效的观点。
我当时不是无法找到任何否定含义或危险信号,建议应避免使用
Optional
。我认为一般的想法是,如果有帮助或改善您的API的可用性,请使用它。评论
stackoverflow.com/questions/25693309-似乎杰克逊已经支持它,因此“不可序列化”不再作为有效理由:)
–Vlasec
15年4月15日在12:52
我建议您提供答案,为什么在方法(更具体地讲,构造函数)的输入参数中使用Optional“不会给我们任何东西”。 dolszewski.com/java/java-8-optional-use-cases包含一个很好的解释。
–吉利
16年8月17日在18:25
我发现,需要将可选结果转换为其他API时会产生一些可选结果。结果是一个相当容易理解的API。见stackoverflow.com/a/31923105/105870
–异教徒卡尔
16-10-12在18:11
链接断开。您能否更新答案?
–softarn
17-10-2在11:57
麻烦的是,尽管开发人员有最好的意图,但它通常并没有改善API。我已经收集了Optional用法错误的示例,所有示例均取自生产代码,您可以检出这些代码。
–MiguelMunoz
18/12/15在3:49
#3 楼
我通常会说,将可选类型用于可以为空的返回值是一个好主意。但是,对于框架,我假设用可选类型替换经典的getter会在使用依赖于getter和setter编码约定的框架(例如Hibernate)时造成很多麻烦。评论
这个建议恰恰是我在stackoverflow.com/a/26328555/3553087中“我们担心热心过度使用的风险”的意思。
–布莱恩·格茨(Brian Goetz)
2014年10月12日18:44
#4 楼
将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中的。 (该示例由Brian Goetz的博客提供。一个更好的示例可能使用
orElse()
方法,因为此代码无论如何都会引发异常,但您可以理解。)但是现在,人们使用Optional的原因非常不同。他们正在使用它来解决语言设计中的缺陷。缺陷在于:无法指定API的哪个参数和返回值允许为空。它可能会在javadocs中提到,但是大多数开发人员甚至都没有为他们的代码编写javadocs,并且很少有人会在编写时检查javadocs。因此,这导致很多代码始终会在使用空值之前检查空值,即使它们经常不可能为空值也是如此,因为它们已经在调用堆栈中重复验证了九到十次。
我认为真正需要解决此缺陷,因为看到新Optional类的许多人都认为它的目的是增加API的清晰度。这就是为什么人们会问诸如“吸气剂应返回可选件”之类的问题吗?不,他们可能不应该这样做,除非您希望将getter用于函数式编程中,否则这种可能性很小。实际上,如果您查看Optional在Java API中的使用位置,那么它主要在Stream类中,Stream类是函数式编程的核心。 (我还没有进行非常彻底的检查,但是Stream类可能是唯一使用它们的地方。)如果您打算在一些功能代码中使用吸气剂,则可能是最好有一个标准的getter,然后再有一个第二个getter,它返回Optional。
哦,如果您需要类可序列化,则绝对不要使用Optional。
可选选项是解决API缺陷的非常糟糕的解决方案,因为a)非常冗长,并且b)从来没有打算首先解决该问题。
更好的API解决方案缺陷是Nullness Checker。这是一个注释处理器,通过使用@Nullable对其进行注释,您可以指定允许哪些参数和返回值为空。这样,编译器可以扫描代码并确定是否将实际上可以为null的值传递给不允许null的值。默认情况下,它假定除非有注释,否则不允许将任何内容都为null。这样,您不必担心空值。将空值传递给参数将导致编译器错误。测试对象的null不能为null会产生编译器警告。这样做的结果是将NullPointerException从运行时错误更改为编译时错误。
这会更改所有内容。
对于您的getter,请不要使用Optional。并尝试设计您的类,以便所有成员都不能为null。也许尝试将Nullness Checker添加到您的项目中,并在需要时声明您的getter和setter参数@Nullable。我只在新项目中做到了这一点。在现有项目中可能会产生很多警告,这些警告中编写了许多多余的null测试,因此可能很难进行改进。但这也会捕获很多错误。我喜欢它。因此,我的代码更加简洁,可靠。
(还有一种新的语言可以解决这个问题。Kotlin编译为Java字节码,允许您指定对象是否可以为null当您声明它时,这是一种更清洁的方法。)
原始帖子(版本2)的附录
经过深思熟虑,我很不情愿地得出一个结论,在一个条件下返回Optional是可以接受的:检索到的值实际上可能为null。我见过很多代码,人们通常从无法正常返回null的getter中返回Optional。我认为这是一种非常糟糕的编码做法,只会增加代码的复杂性,从而更容易出现错误。但是,当返回的值实际上可能为null时,请继续并将其包装在Optional中。
请记住,为函数式编程设计且需要函数引用的方法将(并且应)以两种形式编写,其中一种使用Optional。例如,
Optional.map()
和Optional.flatMap()
都具有函数引用。第一个引用一个普通的getter,第二个引用一个返回Optional的引用。因此,通过在值不能为null的Optional中返回值,您不会帮任何忙。说了这么多,我仍然看到Nullness Checker使用的方法是最好的方法处理空值,因为它们将运行时错误中的NullPointerExceptions转换为编译时错误。
评论
这个答案对我来说似乎最合适。可选仅在添加流时才在Java 8中添加。据我所知,只有流函数返回可选的。
–Archit
18年7月15日在6:03
我认为这是最好的答案之一。人们知道什么是可选的以及它是如何工作的。但是最令人困惑的部分是/在哪里使用以及如何使用它。通过阅读本文,可以消除很多疑问。
–ParagFlume
19年1月15日在4:37
#5 楼
如果您使用的是现代的序列化程序和其他了解Optional
的框架,那么我发现在编写Entity
bean和域层时,这些准则可以很好地发挥作用:如果序列化层(通常是DB)允许表
null
的BAR
列中某个单元格的FOO
值,则吸气剂Foo.getBar()
可以返回Optional
,以指示开发人员可以合理地认为此值为null,他们应该对此进行处理。如果DB保证该值将不为null,则getter不应将其包装在Optional
中。Foo.bar
应该是private
而不是Optional
。如果确实是Optional
,则没有任何理由使用private
。设置器
Foo.setBar(String bar)
应该采用bar
而不是Optional
的类型。如果可以使用null
参数,请在JavaDoc注释中说明。如果不能正常使用null
,IllegalArgumentException
或某些合适的业务逻辑,恕我直言,则更合适。 构造函数不需要
Optional
参数(出于类似于第3点的原因)。通常,我只在构造函数中包括在序列化数据库中必须为非空的参数。为了使上述操作更有效,您可能需要编辑IDE模板以生成getter和
toString()
的相应模板。 ,equals(Obj o)
等。或直接使用这些字段(大多数IDE生成器已经处理了null)。
评论
尽管这可能会吸引有目的的答案,但这是一个好问题。我期待得到有关该主题的真实事实的答案。问题是无效性是否不可避免。组件可能具有允许为null的属性,但是使用该组件的程序员仍然可以决定严格将该属性保持为非null。这样,程序员就不必再处理Optional了。或者换句话说,null确实表示缺少值,就像搜索结果一样(其中Optional是适当的),还是null只是一组可能值中的一个。
另请参阅有关@NotNull批注的讨论:stackoverflow.com/q/4963300/873282