Java 8 getter应该返回可选类型吗?

leo*_*rou 267 java nullable optional java-8

Optional Java 8中引入的类型对于许多开发人员来说是一件新事物.

一个getter方法返回Optional<Foo>类型代替经典的Foo一个好习惯吗?假设值可以null.

Bri*_*etz 470

当然,人们会做他们想做的事.但是在添加此功能时我们确实有明确的意图,并且它不是通用的类型,就像许多人希望我们这样做一样.我们的目的是为库方法返回类型提供一种有限的机制,其中需要一种明确的方式来表示"无结果",并且使用null这种方法绝对可能导致错误.

例如,您可能永远不应该将它用于返回结果数组或结果列表的内容; 而是返回一个空数组或列表.您几乎不应该将它用作某事物或方法参数的字段.

我认为通常使用它作为吸气剂的返回值肯定会过度使用.

可选的应该避免使用它没有任何问题,它不是许多人所希望的那样,因此我们非常关注热情过度使用的风险.

(公共服务公告:永远不要打电话,Optional.get除非你能证明它永远不会是空的;而是使用其中一种安全方法,如orElseifPresent.回想起来,我们应该调用get类似的getOrElseThrowNoSuchElementException东西,或者更清楚地说这是一种非常危险的方法从一开始就破坏了整个目的Optional.经验教训.(更新:Java 10具有Optional.orElseThrow(),在语义上等同于get(),但其名称更合适.))

  • (关于最后一部分)......当我们确信,值永远不会为"null"时,我们可能会使用`orElseThrow(AssertionError :: new)`,ahem或`orElseThrow(NullPointerException :: new)`... (23认同)
  • 如果你的意图是*曾经引入过通用的或者某种类型,那你会做些什么呢?有没有一种方法可选择不适合一个账单,或者只是在新API上引入Optionals会使它成为非Java-ish? (21认同)
  • 通过"通用类型",我的意思是将其构建到语言的类型系统中,而不是提供近似它的库类.(有些语言的类型为T?(T或null)和T!(不可为空的T))可选只是一个类; 我们无法在Foo和Optional <Foo>之间进行隐式转换,因为我们可以使用语言支持. (18认同)
  • 我不知道使用它作为属性的返回值并不是你想要的,直到我在StackOverflow上读到这个答案.事实上,我一直认为,在Oracle网站上阅读本文之后,这正是您的意图:http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html谢谢澄清 (18认同)
  • 我想知道为什么Java世界中的重点是可选的,而不是更好的静态分析.可选确实有一些优点,但是`null`具有的巨大优势是向后兼容性; `Map :: get`返回一个可以为空的`V`,而不是`Optional <V>`,这永远不会改变.不过,它很容易被注释为"@Nullable".现在我们有两种表达缺乏价值的方法,加上真正得到静态分析的动力不足,这似乎是一个更糟糕的位置. (10认同)
  • Brian Goetz回答'你现在后悔java 8中有变化吗?' http://i.stack.imgur.com/LPg1Z.png参见https://jaxenter.de/fragen-und-antworten-zu-java-8-qa-的16':07"-17':35" 33108 (9认同)
  • @BrianGoetz如果它是一个库类,则可选作为通用类型*even*.实际上,大多数具有通用Maybe/Option类型的语言都具有与库类型完全相同的(参见Haskell,Scala,ML,...),不包括Java的N版本 - 尽管它们也具有模式匹配. (8认同)
  • 我仍然没有看到反对从可空成员字段的getter返回Optional的参数.在这种情况下如何过度使用? (7认同)
  • "你可能永远不应该将它用于返回结果数组或结果列表的东西;而是返回一个空数组或列表" - 一个不存在的列表和一个空列表具有不同的语义含义,因此不应该被考虑相同.例如,如果用户不存在,则getBooksOfUser(UserId id)方法将返回空值,但如果用户存在但没有书,则返回空列表; 不一样的. (6认同)
  • @Olayinka答案在某种程度上取决于"什么是吸气剂"的问题 - "吸气剂"和"返回某种东西的方法"之间存在模糊界限.显然,从方法中返回Optional是有用的.但是在现实世界的代码中(无论好坏),大多数getter(比如99%)只是`return x`.在这种情况下,从getter返回一个`Optional`意味着你使用`Optional` _作为你的状态表示_ - 这绝对不是预期的(并且完全属于"热心过度使用"类别.) (4认同)
  • @MiguelMunoz Nope,“为函数式编程而创建”既不是准确的意图也不是有用的意图。 (4认同)
  • 嗯,似乎人们比你想要的更广泛地采用它.当然,人们可以很容易地编写自己的`Maybe`对象,但使用java.util中现有的类更容易.好吧,至少为了你的安慰,我不记得曾经使用过`Optional.get`.我同意将它作为类型系统的一部分非常好. (3认同)
  • 我不明白这一点.我想要Maybe类型和Optional是一个.我为什么要编写自己的,只是因为建议不要将Optional用作字段? (3认同)
  • Brian Goetz:您的原始答案提供了一些示例,说明我们不应该使用可选项,但不应该使用它们.如果您举一个可选返回值绝对是一个好方法的例子,您可以更清楚地说明您的观点. (3认同)
  • @BrianGoetz为什么对一个没有价值的字段使用Optional是一个坏习惯?它在类型系统级别提供额外的语义信息层.在处理对象时非常有用,因为您不需要查找该字段设置的所有位置,以确定它是否始终具有值或者有时它没有值.你似乎使用权威论证来证明这个决定,但我们需要的是一个解释. (3认同)
  • 你的回答没有多大帮助,听起来更像是“因为我这么说......”。从名字上看,Optional 似乎是一种应该用来表示 POJO 中可选字段的类型。当然,如果我们使用Optional,那么我们会假设它永远不会为空。 (3认同)
  • 老实说,我没有看到这个概念有任何重大改进:(我认为能够从任何引用类型(例如通过特殊构造函数)派生空对象会更有用.你将确定*one*state这可以清楚地将对象标识为无用(通过否定ID等).因此,无论何时找不到结果,都会得到相同的Invalid/Unuseful/Empty Object Instance.非常像空集合.可选<T>将你的`null`检查提取到模板代码中.至少在我看来,它不会像使用注释时那样让API看起来更清晰:( (2认同)
  • 可选是为函数式编程而创建的,这就是它应该使用的地方。[这是一个很好的例子](http://mail.openjdk.java.net/pipermail/lambda-dev/2012-September/005952.html),由 Brian Goetz 提供。除了函数式编程之外,可选只会增加代码的复杂性,这只会增加出现错误的可能性。人们使用它来避免在代码中看到 null 一词。这导致代码如下: if(Optional.ofNullable(x).isPresent()) 而不是 if(x!=null)。我会坚持使用 null。 (2认同)
  • @KorayTugay Java 8 只有需要参数的 `Optional.orElseThrow` 版本,如 `optional.orElseThrow(AssertionError::new)`。但是 Java 10 添加了 [无参数版本的 orElseThrow](https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html#orElseThrow()) (2认同)
  • @BrianGoetz Java 并没有发明Optional。Java 的Optional 类型与其他语言中的用法相比也没有什么特别之处。我还在Oracle的网站上发现了https://www.oracle.com/technical-resources/articles/java/java8-Optional.html,建议完全按照您告诉人们不要做的事情进行操作,即对类的字段成员使用Optional 。如果“因为我这么说..”是您的推理,那么我很抱歉,但 Oracle 在这里占据上风。 (2认同)

Jus*_*tin 69

在对我自己进行了一些研究之后,我发现了一些可能暗示适当的事情.最具权威性的是来自Oracle文章的以下引用:

"重要的是要注意Optional类的意图不是替换每个单独的null引用.相反,它的目的是帮助设计更易于理解的API,这样只需读取方法的签名,就可以判断出是否存在可以期待一个可选值.这会强制你主动打开一个Optional来处理缺少值." - 厌倦了空指针异常?考虑使用Java SE 8的可选项!

我还从Java 8中找到了这个摘录:可选:如何使用它

"可选并不意味着在这些情况下使用,因为它不会给我们任何东西:

  • 在域模型层(不可序列化)
  • 在DTO中(同样的原因)
  • 在输入参数的方法
  • 在构造函数参数中"

这也似乎提出了一些有效的观点.

我无法发现任何负面含义或危险信号表明Optional应该避免.我认为一般的想法是,如果它有用或者提高了API的可用性,请使用它.

  • http://stackoverflow.com/questions/25693309/ - 杰克逊似乎已经支持它,因此"不可序列化"不再作为正当理由通过:) (9认同)
  • 我建议您的答案说明为什么在方法(更具体地说是构造函数)的输入参数中使用“Optional”“不会给我们带来任何东西”。http://dolszewski.com/java/java-8-optional-use-cases/包含一个很好的解释。 (2认同)
  • 问题是,尽管开发人员的意图是最好的,但它通常不会改进 API。我一直在收集[Optional的坏用法](https://github.com/SwingGuy1024/Blog/blob/master/Bad%20Uses%20of%20Optional.md)的例子,全部取自生产代码,可以查看出去。 (2认同)

Cla*_*lke 19

我会说一般来说,将可选类型用于可以为空的返回值是一个好主意.然而,对于框架我认为用可选类型替换经典getter在使用依赖于getter和setter的编码约定的框架(例如,Hibernate)时会带来很多麻烦.

  • 在http://stackoverflow.com/a/26328555/3553087中,这个建议正是我所说的"我们担心过度使用的风险". (13认同)

Mig*_*noz 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(...));
Run Code Online (Sandbox Code Playgroud)

比这更清洁:

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;
Run Code Online (Sandbox Code Playgroud)

我的观点是,Optional是为支持函数式编程编写的,它同时被添加到Java中.(这个例子来自Brian Goetz一篇博客.一个更好的例子可能会使用这个orElse()方法,因为这个代码无论如何都会抛出异常,但是你得到了这个.)

但现在,人们使用Optional是出于一个非常不同的原因.他们用它来解决语言设计中的缺陷.缺点是:没有办法指定API的参数和返回值中的哪一个允许为空.它可能在javadocs中提到过,但是大多数开发人员甚至都没有为他们的代码编写javadoc,并且没有多少人会在编写时检查javadoc.因此,这会导致许多代码在使用它们之前始终检查空值,即使它们通常不能为空,因为它们已经在调用堆栈上重复验证了九次或十次.

我认为解决这个漏洞真的很渴望,因为很多人看到新的Optional类的目的是为了增加API的清晰度.这就是为什么人们会问"应该让getters返回Optionals?"这样的问题.不,它们可能不应该,除非您希望将getter用于函数式编程,这是不太可能的.实际上,如果你看一下Java API中使用Optional的地方,它主要是在Stream类中,它们是函数式编程的核心.(我没有仔细检查过,但Stream类可能是他们使用的唯一地方.)

如果您计划在一些功能代码中使用getter,那么最好使用标准的getter和第二个返回Optional的getter.

哦,如果你需要你的类可序列化,你绝对不应该使用Optional.

可选项是API缺陷的一个非常糟糕的解决方案,因为a)它们非常冗长,并且b)它们从来没有打算首先解决这个问题.

Nullness Checker是一个更好的API缺陷解决方案.这是一个注释处理器,允许您通过使用@Nullable注释它们来指定允许哪些参数和返回值为null.这样,编译器可以扫描代码并确定是否将实际为null的值传递给不允许null的值.默认情况下,它假定除非对其进行注释,否则不允许任何内容为空.这样,您不必担心空值.将null值传递给参数将导致编译器错误.测试null为null的对象会产生编译器警告.这样做的结果是将NullPointerException从运行时错误更改为编译时错误.

这改变了一切.

至于你的getter,不要使用Optional.并尝试设计您的类,因此没有成员可能为null.并且可以尝试将Nullness Checker添加到您的项目中,并在需要时声明您的getter和setter参数@Nullable.我只用新项目完成了这项工作.它可能会在现有的项目中产生很多警告,这些项目都是用null进行大量多余的测试,所以改造可能很难.但它也会遇到很多错误.我喜欢它.因为它,我的代码更清晰,更可靠.

(还有一种新的语言可以解决这个问题.编译为Java字节代码的Kotlin允许您在声明对象时指定对象是否为空.这是一种更清晰的方法.)

原帖(第2版)附录

经过深思熟虑之后,我不情愿地得出结论:在一个条件下返回Optional是可以接受的:检索到的值实际上可能为null.我看过很多代码,人们经常从getter返回Optional,这些getter不可能返回null.我认为这是一种非常糟糕的编码实践,只会增加代码的复杂性,从而使错误更容易发生.但是当返回的值实际上可能为null时,请继续将其包装在Optional中.

请记住,为函数式编程而设计的方法以及需要函数引用的方法将(并且应该)以两种形式编写,其中一种使用Optional.例如,Optional.map()并且Optional.flatMap()都接受函数引用.第一个引用普通的getter,第二个引用一个返回Optional的引用.因此,如果值不能为null,则返回可选项,而不是帮助任何人.

说了这么多,我仍然看到Nullness Checker使用的方法是处理空值的最佳方法,因为它们将NullPointerExceptions从运行时错误转换为编译时错误.

  • 这个答案对我来说似乎最合适。Optional 仅在 java 8 添加流时添加。据我所知,只有流函数返回可选。 (2认同)