因为Java 1.8返回Optional
对象比抛出异常更可取吗?
我越来越多地看到这样的代码:
public Optional<?> get(int i) {
// do somtething
Object result = ...
Optional.ofNullable(result);
}
而不是:
public Object get(int i) {
if(i<0 || i>=size) {
throw new IndexOutOfBoundsException("Index: " + i + ". Size: " + size);
}
// do somtething
Object result = ...
return result;
}
这是否意味着我们需要忘记旧方法并使用新方法?哪个Optional
适合?
答案 0 :(得分:35)
您提供的示例不是Optional的适当用法。空Optional表示由于调用者无法预测的原因而缺少的值。这是法律调用方法的结果。
您作为“旧习语”呈现的代码会执行输入验证,如果输入无效,则会抛出未经检查的异常。即使您引入Optional,此行为也应保持不变。唯一的区别是Object get(i)
的返回值可能为null,而Optional<?> get(i)
的返回值永远不为null,因为Optional实例的特殊状态表示缺少值。
返回Optional而不是可空值的方法的优点是消除样板代码,该代码必须在尝试对返回值执行任何操作之前进行例行的空检查。纯粹在方法中使用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的未来版本可能会使这个可行。
答案 1 :(得分:24)
可以平等地滥用异常,空值和可选项。在这种特殊情况下,我认为您可能会滥用可选项,因为您会默默地隐藏前提条件违规并将其转换为正常返回。在收到代码中的空选项后,调用者无法区分&#34;我正在寻找的东西不在那里&#34;和#34;我问了一个无效的问题。&#34;
因为Optional是新的,所以它也有过度使用的趋势;希望随着时间的推移,正确的模式将被内化。
可选是 null对象模式的示例;它提供了一种安全的方式来说&#34;什么都没有&#34;当什么时候没有&#34;是一个合理的预期结果。 (返回一个空数组或一个空集合在这些域中是类似的例子。)无论你想表示什么,那么什么都没有&#34; by null / optional vs a exception通常是&#34;是否存在&#34;是一种普遍预期的情况,还是特殊情况。例如,如果映射不存在,则没有人希望Map.get
抛出异常;映射不存在是预期的,而不是例外的结果。 (如果我们在1997年有Optional
,则Map.get
可能会返回Optional
。)
我不知道你在哪里听到了关于例外情况可选择优先权的建议,但是谁告诉你这些意见不明智。如果你之前抛出异常,你可能仍然会抛出异常;如果您之前返回null,则可以考虑返回Optional
。
答案 2 :(得分:3)
在可能出现错误的情况下,尝试使用合适的数据类型。
Try不使用抽象'present'或'empty',而是使用抽象'失败'或'成功'。
由于Java 8没有开箱即用,因此有必要使用一些3.方库。 (也许我们会在Java 9中看到它添加?)