现在已经使用Java 8大约6个月了,我仍然没有信心何时使用 Optional
. 我似乎想要在任何地方使用它可能是 null
,而且根本没有 .
似乎有很多情况我可以使用它,我不知道它是否增加了好处(可读性/无效安全性)或只是导致额外的开销 .
所以,我有一些例子,我对 Optional
是否有益的想法 .
1 - 当方法可以返回 null
时,作为公共方法返回类型:
public Optional<Foo> findFoo(String id);
2 - 当param可能是 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
在集合中是否有任何好的用途?
我错过了什么案例?
13 回答
如果你不喜欢这样检查
那么你可以这样做,这就是我所看到的
基本上,它给出了一种幻觉,即您可以更有效地处理空值 .
我认为Guava Optional和他们的wiki页面相当不错:
Optional
增加了一些开销,但我认为它的明显优势是明确表示某个对象可能不存在并强制程序员处理这种情况 . 它可以防止有人忘记心爱的!= null
检查 .以2为例,我认为编写更明确的代码:
比
对我而言,
Optional
更好地捕捉到没有声卡存在的事实 .关于你的观点我的2¢:
public Optional<Foo> findFoo(String id);
- 我不确定这个 . 也许我会返回Result<Foo>
,它可能是空的或包含Foo
. 这是一个类似的概念,但不是真正的Optional
.public Foo doSomething(String id, Optional<Bar> barOptional);
- 我更喜欢@Nullable和一个findbugs检查,如Peter Lawrey's answer - 另见this discussion .你的书籍例子 - 我不确定我是否会在内部使用Optional,这可能取决于复杂性 . 对于一本书的"API",我会使用
Optional<Index> getIndex()
明确指出该书可能没有索引 .我不会在集合中使用它,而不是在集合中允许空值
一般来说,我会尽量减少绕过
null
的传递 . (一旦被烧掉......)我认为找到合适的抽象是值得的,并向其他程序员表明某个返回值实际代表什么 .关于此功能已有多种资源:
Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional
Option type:"a polymorphic type that represents encapsulation of an optional value"
Java 8 Optional: How to Use it
The Design of Optional
Monadic Java
Processing data with Java SE 8 Streams
Optional
class允许您避免使用null
并提供更好的替代方案:这鼓励开发者对存在进行检查,以避免被捕获
NullPointerException
.API变得更好记录,因为它可以看到,在哪里可以缺少值 .
Optional
为进一步使用该对象提供了方便的API:isPresent(); get(); orElse(); orElseGet(); orElseThrow(); map(); filter(); flatmap() .此外,许多框架主动使用此数据类型并从其API返回 .
Optional
的要点是为函数提供一种返回值的方法,以指示缺少返回值 . 见this discussion . 这允许调用者继续一系列流畅的方法调用 .这与OP的问题中的用例 #1 最匹配 . 虽然缺少值是比null更精确的公式,因为像
IntStream.findFirst
这样的东西永远不会返回null .对于用例 #2 ,将可选参数传递给方法,可以使其工作,但它相当笨拙 . 假设您有一个方法,它接受一个字符串后跟一个可选的第二个字符串 . 接受
Optional
作为第二个arg会产生如下代码:即使接受null也更好:
可能最好的方法是使用一个重载方法接受单个字符串参数并为第二个提供默认值:
这确实有局限性,但它比上述任何一个都要好得多 .
用例 #3 和 #4 ,在类字段或数据结构中具有
Optional
被认为是对API的滥用 . 首先,它违背了Optional
的主要设计目标,如顶部所述 . 其次,它没有增加任何 Value .有三种方法可以处理_472799中缺少值:提供替换值,调用函数以提供替换值或抛出异常 . 如果你're storing into a field, you' d在初始化或分配时执行此操作 . 如果您正在将值添加到列表中,正如OP所提到的那样,您可以选择不添加值,从而使"flattening"缺少值 .
我敢肯定有人可能会想出一些他们真正想要在场或集合中存储
Optional
的人为案例,但总的来说,最好避免这样做 .似乎
Optional
仅在Optional中的类型T是int
,long
,char
等原始类型时才有用 . 对于"real"类,它对我来说没有意义,因为无论如何你都可以使用null
值 .我认为这是从这里(或从另一个类似的语言概念) .
Nullable<T>
在C#中,很久以前就引入了
Nullable<T>
来包装值类型 .我认为Optional不是可能返回null值的方法的一般替代品 .
基本思想是:缺少 Value 并不意味着它可能在未来可用 . 这是findById(-1)和findById(67)之间的区别 .
调用者的Optionals的主要信息是他可能不会指望给定的值,但可能在某个时间可用 . 也许它会再次消失并再次回来 . 这就像一个开/关开关 . 您可以使用“选项”来打开或关闭灯 . 但如果您没有打开灯,则无法选择 .
所以我觉得在任何地方引入可能会返回null之前的Optionals太乱了 . 我仍然会使用null,但仅限于树的根,延迟初始化和显式查找方法等限制区域 .
就个人而言,我更喜欢使用IntelliJ's Code Inspection Tool来使用
@NotNull
和@Nullable
检查,因为这些检查主要是编译时间(可以进行一些运行时检查) . 这在代码可读性和运行时性能方面具有较低的开销 . 它不像使用Optional那样严格,但是缺乏严谨性应该得到适当的单元测试的支持 .这适用于Java 5,无需包装和解包值 . (或创建包装器对象)
Java SE 8引入了一个名为java.util.Optional的新类,
您可以使用null值创建空Optional或Optional .
这是一个带有非null值的Optional:
Do Something If a Value Is Present
现在您有了一个Optional对象,您可以访问可用的方法来显式处理值的存在与否 . 而不必记住进行空检查,如下所示:
您可以使用ifPresent()方法,如下所示:
来自Oracle tutorial:
我值得,我想加上我的2美分 . 他们反对design goal of Optional,这是由Stuart Marks's answer很好地总结,但我仍然相信它们的有效性(显然) .
使用Optional Everywhere
一般
我写了一整个blog post about using Optional但它基本上归结为:
设计你的课程,以避免在可能的情况下选择性
在所有剩余的情况下,默认应该是使用
Optional
而不是null
可能会例外:
局部变量
返回私有方法的值和参数
性能关键代码块(没有猜测,使用分析器)
前两个异常可以减少
Optional
中包装和解包引用的感知开销 . 选择它们使得null永远不会合法地将边界从一个实例传递到另一个实例 .请注意,这几乎不会允许集合中的
Optional
s几乎与null
一样糟糕 . 只是不要这样做 . ;)关于你的问题
是的 .
如果超载是没有选择的,是的 .
如果其他方法(子类化,装饰......)别无选择,是的 .
请不要!
优点
这样做可以减少代码库中
null
的存在,尽管它不能消除它们 . 但这甚至不是主要观点 . 还有其他重要的优点:澄清意图
使用
Optional
清楚地表明该变量是可选的 . 您的代码的任何读者或您的API的消费者都将遭受殴打,因为在访问该值之前可能没有任何内容并且需要进行检查 .消除不确定性
如果没有
Optional
,null
事件的含义尚不清楚 . 它可能是状态的合法表示(请参阅Map.get)或初始化缺失或失败的实现错误 .随着
Optional
的持续使用,这种情况发生了巨大变化 . 在这里,null
的出现已经表明存在错误 . (因为如果允许丢失该值,则会使用Optional
. )这使得调试空指针异常变得更加容易,因为已经回答了null
的含义问题 .更多空检查
既然没有任何东西可以
null
了,这可以在任何地方强制执行 . 无论是注释,断言还是普通检查,您都不必考虑此参数或返回类型是否为null . 它不能!缺点
当然,没有银弹......
表现
将值(尤其是原语)包装到额外的实例中会降低性能 . 在紧密的循环中,这可能变得明显甚至更糟 .
请注意,编译器可能能够绕过
Optional
s的短寿命的额外引用 . 在Java 10中,value types可能会进一步减少或消除惩罚 .序列化
Optional is not serializable但workaround并不过分复杂 .
不变性
由于Java中泛型类型的不变性,当将实际值类型推入泛型类型参数时,某些操作会变得很麻烦 . 给出了一个例子here (see "Parametric polymorphism") .
这是一个有趣的用法(我相信)...测试 .
我打算对我的一个项目进行大量测试,因此我 Build 了断言;只有我需要验证的东西和其他我没有验证的东西 .
因此,我构建了断言并使用断言来验证它们的东西,如下所示:
在NodeAssert类中,我这样做:
这意味着如果我想检查标签,断言真的只会触发!
这是good article,它显示了用例#1的用处 . 有这个代码
转化为这个
通过使用Optional作为相应getter方法的返回值 .