我已经使用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>>

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

有我错过的案子吗?


当前回答

可选类让你避免使用null,并提供一个更好的选择:

这鼓励开发人员检查是否存在,以避免未捕获的NullPointerException。 API有了更好的文档记录,因为它可以看到,在哪里可以期望那些可能缺失的值。

Optional为进一步处理对象提供了方便的API: isPresent ();get ();orElse ();orElseGet ();orElseThrow ();map ();filter ();flatmap()。

此外,许多框架积极地使用这种数据类型并从它们的API中返回它。

其他回答

Oracle教程:

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

我不认为Optional是可能返回空值的方法的一般替代品。

基本思想是:一个值的缺失并不意味着它在未来可能是可用的。它是findById(-1)和findById(67)之间的差值。

可选项对于调用者的主要信息是,他可能不指望给定的值,但它可能在某个时间可用。也许它会再次消失,然后再回来一次。这就像一个开/关开关。你可以“选择”打开或关闭灯。但是如果你没有灯可以打开,你就别无选择了。

所以我发现它太乱引入可选的地方,以前可能返回null。我仍然会使用null,但是只在一些受限的地方,比如树的根、惰性初始化和显式查找方法。

这里有一个关于……的有趣用法(我认为)。测试。

我打算对我的一个项目进行大量测试,因此我构建断言;只是有些事我必须核实,有些事我不需要。

因此,我构建了一些东西来断言,并使用断言来验证它们,如下所示:

public final class NodeDescriptor<V>
{
    private final Optional<String> label;
    private final List<NodeDescriptor<V>> children;

    private NodeDescriptor(final Builder<V> builder)
    {
        label = Optional.fromNullable(builder.label);
        final ImmutableList.Builder<NodeDescriptor<V>> listBuilder
            = ImmutableList.builder();
        for (final Builder<V> element: builder.children)
            listBuilder.add(element.build());
        children = listBuilder.build();
    }

    public static <E> Builder<E> newBuilder()
    {
        return new Builder<E>();
    }

    public void verify(@Nonnull final Node<V> node)
    {
        final NodeAssert<V> nodeAssert = new NodeAssert<V>(node);
        nodeAssert.hasLabel(label);
    }

    public static final class Builder<V>
    {
        private String label;
        private final List<Builder<V>> children = Lists.newArrayList();

        private Builder()
        {
        }

        public Builder<V> withLabel(@Nonnull final String label)
        {
            this.label = Preconditions.checkNotNull(label);
            return this;
        }

        public Builder<V> withChildNode(@Nonnull final Builder<V> child)
        {
            Preconditions.checkNotNull(child);
            children.add(child);
            return this;
        }

        public NodeDescriptor<V> build()
        {
            return new NodeDescriptor<V>(this);
        }
    }
}

在NodeAssert类中,我这样做:

public final class NodeAssert<V>
    extends AbstractAssert<NodeAssert<V>, Node<V>>
{
    NodeAssert(final Node<V> actual)
    {
        super(Preconditions.checkNotNull(actual), NodeAssert.class);
    }

    private NodeAssert<V> hasLabel(final String label)
    {
        final String thisLabel = actual.getLabel();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is null! I didn't expect it to be"
        ).isNotNull();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is not what was expected!\n"
            + "Expected: '%s'\nActual  : '%s'\n", label, thisLabel
        ).isEqualTo(label);
        return this;
    }

    NodeAssert<V> hasLabel(@Nonnull final Optional<String> label)
    {
        return label.isPresent() ? hasLabel(label.get()) : this;
    }
}

这意味着断言只在我想检查标签时才会触发!

我认为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值。(一旦烧…) 我认为有必要找到适当的抽象,并向程序员同事指出某个返回值实际代表什么。

可选类让你避免使用null,并提供一个更好的选择:

这鼓励开发人员检查是否存在,以避免未捕获的NullPointerException。 API有了更好的文档记录,因为它可以看到,在哪里可以期望那些可能缺失的值。

Optional为进一步处理对象提供了方便的API: isPresent ();get ();orElse ();orElseGet ();orElseThrow ();map ();filter ();flatmap()。

此外,许多框架积极地使用这种数据类型并从它们的API中返回它。