你所提供的示例并不是Optional的适当使用方式。空的Optional表示一个值因为调用者无法预测的原因而不存在。它是方法合法调用的结果。
你提供的“旧惯例”代码对输入进行验证,如果输入无效,则抛出未检查的异常。即使引入Optional,这种行为也应该保持不变。唯一的区别是
Object get(i)
的返回值可能为空,而
Optional<?> get(i)
的返回值永远不会为空,因为Optional实例存在一种特殊状态,表示缺少值。
使用返回Optional而不是可空值的方法的优点在于消除了必须在尝试处理返回值之前进行例行的null检查的样板代码。仅在方法内部纯粹使用Optional还有许多其他优点。例如:
static Optional<Type> componentType(Type type) {
return Optional.of(type)
.filter(t -> t instanceof ParameterizedType)
.map(t -> (ParameterizedType) t)
.filter(t -> t.getActualTypeArguments().length == 1)
.filter(t -> Optional.of(t.getRawType())
.filter(rt -> rt instanceof Class)
.map(rt -> (Class<?>) rt)
.filter(Stream.class::isAssignableFrom)
.isPresent())
.map(t -> t.getActualTypeArguments()[0]);
这里最重要的好处是完美的作用域控制:在每个新作用域中,相同名称t
被重新使用为适合处理阶段的变量类型。因此,与其被迫使变量在其有用寿命过期后仍然在作用域内,并为每个后续变量发明新名称,使用这种习惯用法可以确切地获得我们需要继续进行的最小限度。
只是为了增加趣味性,您可以完全基于Optional来实现equals
:
@Override public boolean equals(Object obj) {
return Optional.ofNullable(obj)
.filter(that -> that instanceof Test)
.map(that -> (Test)that)
.filter(that -> Objects.equals(this.s1, that.s1))
.filter(that -> Objects.equals(this.s2, that.s2))
.isPresent();
}
虽然我认为这个习语非常简洁易读,但目前还不够优化,不能作为一个值得推荐的生产级选择。未来版本的Java可能会使其可行。
return null;
而不是抛出异常。但在两种情况下都取决于具体情况。使用Optional而不是null的想法是让缺失更加明确,并减少NullPointerExceptions的风险。 - eckes