我已经使用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>>
添加任何东西-特别是因为一个人可以使用过滤器()删除空值等,但在集合中有任何可选的好用途吗?
有我错过的案子吗?
这里有一个关于……的有趣用法(我认为)。测试。
我打算对我的一个项目进行大量测试,因此我构建断言;只是有些事我必须核实,有些事我不需要。
因此,我构建了一些东西来断言,并使用断言来验证它们,如下所示:
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;
}
}
这意味着断言只在我想检查标签时才会触发!
在java中,除非你沉迷于函数式编程,否则不要使用它们。
它们没有作为方法参数的位置(我保证有一天会有人传递给你一个空的可选参数,而不仅仅是一个空的可选参数)。
它们对返回值有意义,但它们会让客户端类继续延伸行为构建链。
FP和链在像java这样的命令式语言中几乎没有位置,因为它使调试变得非常困难,而不仅仅是阅读。当你走到这一行时,你无法知道程序的状态和意图;你必须一步一步地找出它(尽管有步骤过滤器,但通常不是你的代码和许多堆栈帧深),你必须添加大量断点,以确保它可以停止在你添加的code/lambda中,而不是简单地走if/else/call琐碎的行。
如果你想要函数式编程,选择java以外的语言,并希望你有调试它的工具。
我认为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值。(一旦烧…)
我认为有必要找到适当的抽象,并向程序员同事指出某个返回值实际代表什么。