用于可选

新手上路,请多包涵

现在已经使用 Java 8 6 个多月了,我对新的 API 更改非常满意。我仍然不确定的一个领域是何时使用 Optional 。我似乎在想在任何地方都使用它之间摇摆不定,可能是 null 和根本没有。

似乎在很多情况下我都可以使用它,但我不确定它是否会增加好处(可读性/空安全性)或只会导致额外的开销。

所以,我有几个例子,我很想知道社区对 Optional 是否有益的想法。

1 - 当方法可以返回时作为公共方法返回类型 null

 public Optional<Foo> findFoo(String id);

2 - 当参数可能是 null 时作为方法参数:

 public Foo doSomething(String id, Optional<Bar> barOptional);

3 - 作为 bean 的可选成员:

 public class Book {

  private List<Pages> pages;
  private Optional<Index> index;

}

4 - 在 Collections

一般来说,我不认为:

 List<Optional<Foo>>

添加任何东西 - 特别是因为可以使用 filter() 删除 null 值等,但是 Optional 在集合中有什么好的用途吗?

有什么我错过的案例吗?

原文由 Will 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 458
2 个回答

Optional 的主要设计目标是为函数返回值提供一种方法,以指示没有返回值。请参阅 此讨论这允许调用者继续一系列流畅的方法调用。

这与 OP 问题中的用例 #1 最接近。虽然, 没有值 是比 null 更精确的公式,因为像 IntStream.findFirst 这样的东西永远不会返回 null。


对于用例 #2 ,将可选参数传递给方法,这可以使其工作,但它相当笨拙。假设您有一个方法,该方法接受一个字符串,后跟一个可选的第二个字符串。接受 Optional 作为第二个参数将产生如下代码:

 foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

即使接受 null 也更好:

 foo("bar", "baz");
foo("bar", null);

可能最好的方法是拥有一个接受单个字符串参数并为第二个提供默认值的重载方法:

 foo("bar", "baz");
foo("bar");

这确实有局限性,但它比上述任何一个都好得多。

用例 #3#4 在类字段或数据结构中具有 Optional 被视为对 API 的滥用。首先,它违背了 Optional 的主要设计目标,如顶部所述。其次,它没有增加任何价值。

处理 Optional 中缺少值的三种方法:提供替代值、调用函数以提供替代值或抛出异常。如果要存储到字段中,则应在初始化或分配时执行此操作。如果您将值添加到列表中,正如 OP 所提到的,您可以选择简单地不添加值,从而“拉平”不存在的值。

我敢肯定,有人可能会想出一些人为的案例,他们真的想在字段或集合中存储 Optional ,但总的来说,最好避免这样做。

原文由 Stuart Marks 发布,翻译遵循 CC BY-SA 4.0 许可协议

我来晚了,但为了它的价值,我想加上我的 2 美分。它们违背了 Optional 的设计目标, Stuart Marks 的回答 对此进行了很好的总结,但我仍然相信它们的有效性(显然)。

随处使用可选

一般来说

我写了 一篇关于使用 Optional 的完整博客文章, 但基本上可以归结为:

  • 尽可能设计你的课程以避免可选性
  • 在所有其余情况下,默认应使用 Optional 而不是 null
  • 可能例外:
    • 局部变量
    • 私有方法的返回值和参数
    • 性能关键代码块(不用猜测,使用分析器)

前两个异常可以减少 Optional 中包装和解包引用的感知开销。选择它们使得 null 永远无法合法地将边界从一个实例传递到另一个实例。

请注意,这几乎永远不会允许 Optional s 在集合中,这几乎和 null s 一样糟糕。只是不要这样做。 ;)

关于您的问题

  1. 是的。
  2. 如果超载是没有选择的,是的。
  3. 如果其他方法(子类化、装饰等)都行不通,那就行。
  4. 请不!

优点

这样做会减少代码库中 null 的存在,尽管它不会根除它们。但这甚至不是重点。还有其他重要的优点:

阐明意图

使用 Optional 清楚地表明该变量是可选的。您的代码的任何读者或您的 API 的消费者都会被这样一个事实所困扰,即那里可能什么都没有,并且在访问该值之前必须进行检查。

消除不确定性

没有 Optional 的含义 null 出现不清楚。它可以是状态的合法表示(请参阅 Map.get )或实施错误,如丢失或失败的初始化。

随着 Optional 的持续使用,这种情况发生了巨大变化。在这里,已经出现 null 表示存在错误。 (因为如果允许该值丢失,则会使用 Optional 。)这使得调试空指针异常更加容易,因为这个 null 的含义问题是已经回答了。

更多空检查

现在没有什么可以 null 了,这可以在任何地方强制执行。无论是使用注解、断言还是普通检查,您永远不必考虑这个参数或那个返回类型是否可以为 null。不能!

缺点

当然,没有银弹…

表现

将值(尤其是原语)包装到一个额外的实例中会降低性能。在紧密循环中,这可能会变得很明显,甚至更糟。

请注意,编译器可能能够绕过 Optional 的短暂生命周期的额外引用。在 Java 10 中, 值类型 可能会进一步减少或消除惩罚。

连载

Optional 不可序列化,但 解决方法 并不过分复杂。

不变性

由于 Java 中泛型类型的不变性,当实际值类型被推入泛型类型参数时,某些操作变得很麻烦。 这里给出了一个例子(参见“参数多态性”)

原文由 Nicolai Parlog 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题