可选vs抛出异常

时间:2015-03-01 16:57:28

标签: java java-8 optional

因为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适合?

3 个答案:

答案 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中看到它添加?)

Try for Java