我已经使用Java 8 6个多月了,我对新的API变化非常满意。我仍然不确定的一个领域是什么时候使用Optional。我似乎在想要在任何地方使用它之间摇摆,有些东西可能是空的,而根本没有。

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

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

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

public Optional<Foo> findFoo(String id);

2 -当参数可以为空时,作为方法参数:

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

3 -作为bean的可选成员:

public class Book {

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

}

4 -收集:

总的来说,我不认为:

List<Optional<Foo>>

添加任何东西-特别是因为一个人可以使用过滤器()删除空值等,但在集合中有任何可选的好用途吗?

有我错过的案子吗?


当前回答

Optional与Iterator设计模式的不可修改实例具有类似的语义:

它可能引用也可能不引用对象(如isPresent()所给出的) 如果它确实引用了一个对象,则可以解除引用(使用get()) 但是它不能被提升到序列中的下一个位置(它没有next()方法)。

因此,在您以前可能考虑使用Java迭代器的情况下,请考虑返回或传递一个Optional。

其他回答

我认为Guava Optional和他们的维基页面说得很好:

Besides the increase in readability that comes from giving null a name, the biggest advantage of Optional is its idiot-proof-ness. It forces you to actively think about the absent case if you want your program to compile at all, since you have to actively unwrap the Optional and address that case. Null makes it disturbingly easy to simply forget things, and though FindBugs helps, we don't think it addresses the issue nearly as well. This is especially relevant when you're returning values that may or may not be "present." You (and others) are far more likely to forget that other.method(a, b) could return a null value than you're likely to forget that a could be null when you're implementing other.method. Returning Optional makes it impossible for callers to forget that case, since they have to unwrap the object themselves for their code to compile. -- (Source: Guava Wiki - Using and Avoiding null - What's the point?)

可选增加了一些开销,但我认为它的明显优势是使其显式化 一个对象可能不存在,它强制程序员处理这种情况。它可以防止有人忘记心爱的人!=空支票。

以2为例,我认为这是更明确的代码:

if(soundcard.isPresent()){
  System.out.println(soundcard.get());
}

if(soundcard != null){
  System.out.println(soundcard);
}

对我来说,可选的更好地抓住了没有声卡的事实。

我对你的观点有2个看法:

public Optional<Foo> findFoo(String id); - I am not sure about this. Maybe I would return a Result<Foo> which might be empty or contain a Foo. It is a similar concept, but not really an Optional. public Foo doSomething(String id, Optional<Bar> barOptional); - I would prefer @Nullable and a findbugs check, as in Peter Lawrey's answer - see also this discussion. Your book example - I am not sure if I would use the Optional internally, that might depend on the complexity. For the "API" of a book, I would use an Optional<Index> getIndex() to explicitly indicate that the book might not have an index. I would not use it in collections, rather not allowing null values in collections

一般来说,我会尽量减少传递null值。(一旦烧…) 我认为有必要找到适当的抽象,并向程序员同事指出某个返回值实际代表什么。

Oracle教程:

Optional的目的不是替换代码库中的每一个空引用,而是帮助设计更好的api,使用户可以通过读取方法的签名来判断是否需要一个可选值。此外,Optional强制您主动展开Optional以处理缺少值的情况;因此,可以保护代码不受意外空指针异常的影响。

Optional与Iterator设计模式的不可修改实例具有类似的语义:

它可能引用也可能不引用对象(如isPresent()所给出的) 如果它确实引用了一个对象,则可以解除引用(使用get()) 但是它不能被提升到序列中的下一个位置(它没有next()方法)。

因此,在您以前可能考虑使用Java迭代器的情况下,请考虑返回或传递一个Optional。

就我个人而言,我更喜欢使用IntelliJ的代码检查工具来使用@NotNull和@Nullable检查,因为这些检查主要是在编译时进行的(可以有一些运行时检查),这在代码可读性和运行时性能方面具有较低的开销。它不像使用Optional那样严格,但是这种缺乏严格性应该得到适当的单元测试的支持。

public @Nullable Foo findFoo(@NotNull String id);

public @NotNull Foo doSomething(@NotNull String id, @Nullable Bar barOptional);

public class Book {

  private List<Pages> pages;
  private @Nullable Index index;

}

List<@Nullable Foo> list = ..

这适用于Java 5,不需要包装和展开值。(或创建包装器对象)

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

这是Optional的预期用例,如JDK API文档中所示:

Optional主要用于作为方法返回类型,其中 显然需要表示“没有结果”,并且在其中使用null 很可能造成错误。

Optional表示两种状态之一:

它有一个值(isPresent返回true) 它没有值(isEmpty返回true)

因此,如果您有一个方法返回一些东西或不返回,这就是Optional的理想用例。

这里有一个例子:

Optional<Guitarist> findByLastName(String lastName);

此方法接受一个用于在数据库中搜索实体的参数。有可能不存在这样的实体,因此使用Optional返回类型是一个好主意,因为它迫使调用该方法的人考虑空场景。这减少了出现NullPointerException的机会。

2 -当参数可以为空时,作为方法参数:

尽管在技术上是可能的,但这不是Optional的预期用例。

让我们考虑一下您提议的方法签名:

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

主要的问题是我们可以调用doSomething,其中barOptional有三种状态之一:

a可选值,例如doSomething("123",可选。(新酒吧()) 可选的,例如doSomething("123", Optional.empty()) 例如:doSomething("123", null)

这3种状态需要在方法实现中适当地处理。

更好的解决方案是实现重载方法。

public Foo doSomething(String id);

public Foo doSomething(String id, Bar bar);

这使得API的使用者可以非常清楚地知道调用哪个方法,并且不需要传递null。

3 -作为bean的可选成员:

给定示例Book类:

public class Book {
  private List<Pages> pages;
  private Optional<Index> index;
}

Optional类变量遇到了与上面讨论的Optional方法参数相同的问题。它可以有三种状态之一:存在、空或null。

其他可能的问题包括:

Serializable:如果你实现Serializable并尝试序列化这个类的对象,你会遇到java.io.NotSerializableException,因为Optional不是为这个用例设计的 转换为JSON:当序列化为JSON时,可选字段可能会以不希望的方式映射,例如{"empty":false,"present":true}。 尽管如果您使用流行的Jackson库,它确实为这个问题提供了一个解决方案。

尽管存在这些问题,Oracle还是在2014年发布Java 8 Optional时发布了这篇博文。它包含了对类变量使用Optional的代码示例。

public class Computer {
  private Optional<Soundcard> soundcard;  
  public Optional<Soundcard> getSoundcard() { ... }
  ...
}

在接下来的几年里,开发人员找到了更好的替代方法,比如实现getter方法来创建Optional对象。

public class Book {
    private List<Pages> pages;
    private Index index;
    public Optional<Index> getIndex() {
        return Optional.ofNullable(index);
    }
}

在这里,我们使用ofNullable方法返回一个带有值的Optional,如果index是非空的,或者返回一个空的Optional。

4 -收集:

我同意创建一个可选列表(例如List<Optional<Foo>>)不会添加任何东西。 相反,如果该项目不存在,则不要将其包含在列表中。