可选用途

2022-08-31 04:54:15

我已经使用Java 8超过6个月了,我对新的API更改非常满意。我仍然没有信心的一个领域是何时使用。我似乎在想要在任何地方使用它之间摇摆不定,而根本没有。Optionalnull

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

所以,我举了几个例子,我对社区关于是否有益的想法很感兴趣。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()nullOptional

我错过了什么案例?


答案 1

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

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


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

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

即使接受 null 也更好:

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

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

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

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

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

有三种方法可以处理中缺少值:提供替换值,调用函数以提供替代值,或引发异常。如果要存储到字段中,则需要在初始化或分配时执行此操作。如果要将值添加到列表中,如OP所述,您有额外的选择,即根本不添加值,从而“扁平化”出不存在的值。Optional

我相信有人可能会想出一些人为的案例,他们真的想在田野或集合中存储一个,但总的来说,最好避免这样做。Optional


答案 2

我迟到了,但为了它的价值,我想添加我的2美分。它们与Austant的设计目标背道而驰,Stuart Marks的答案很好地总结了这一点,但我仍然相信它们的有效性(显然)。

在任何地方使用可选

通常

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

  • 尽可能设计类以避免可选性
  • 在所有剩余情况下,默认值应为使用而不是Optionalnull
  • 可能为以下情况破例:
    • 局部变量
    • 将值和参数返回给私有方法
    • 性能关键型代码块(无猜测,使用探查器)

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

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

关于您的问题

  1. 是的。
  2. 如果重载是无选择,则为。
  3. 如果其他方法(子类化,装饰等)没有选择,是的。
  4. 请不要!

优势

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

阐明意图

使用清楚地表示变量是可选的。任何代码的读者或API的消费者都会被殴打,因为那里可能没有任何东西,并且在访问值之前需要检查。Optional

消除不确定性

没有事件的含义是不清楚的。它可能是某个州的法律代表(请参阅 Map.get)或实现错误,如初始化缺失或失败。Optionalnull

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

更多空检查

现在什么都不能再了,这可以在任何地方强制执行。无论是使用注释、断言还是普通检查,您都不必考虑此参数或该返回类型是否可以为 null。它不能!null

当然,没有银弹...

性能

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

请注意,编译器可能能够规避 s 的短期生存期的额外引用。在Java中,10值类型可能会进一步减少或删除惩罚。Optional

序列化

可选是不可序列化的,解决方法并不过分复杂。

不变性

由于 Java 中泛型类型的不变性,当将实际值类型推送到泛型类型参数中时,某些操作变得繁琐。这里给出了一个示例(参见“参数化多态性”)。


推荐