5

Guava 提供了辅助函数来检查先决条件,但我找不到辅助函数来检查中间结果。

private void foo(String param)
{
    checkNotNull(param, "Required parameter is not set");

    int x = get(param);
    if (x == -1) {
        throw new RuntimeException("This should never have happened and indicates a bug.");
    }
}
  • 我应该将零件包装if (...) {....}在自己的助手中吗?
  • 还是我应该使用checkState番石榴?
  • 还是我应该将失败get()视为param和使用的结果checkArgument
  • 在这些情况下我应该使用断言吗?
  • 还是我错过了什么?
4

4 回答 4

7

它介于偏好问题和惯例问题之间。

一般人们会用断言来表示编程错误;也就是说,“如果我的工作做得对,那么无论用户输入或其他外部力量如何,非空值param不会导致 -1 from 。” get我几乎将它们视为可以在运行时选择性地验证的注释。

另一方面,如果get在某些情况下可能返回 -1,但该输入无效,那么我通常会抛出一个IllegalArgumentException, 并且checkArgument是一种完全合理的方法。这样做的一个缺点是,当你后来发现它时,它可能来自几乎任何地方。考虑:

try {
    baz();
    bar();
    foo(myInput);
} catch (IllegalArgumentException e) {
    // Where did this come from!?
    // It could have come from foo(myInput), or baz(), or bar(),
    // or some method that any of them invoked, or really anywhere
    // in that stack.
    // It could be something totally unrelated to user input, just
    // a bug somewhere in my code.
    // Handle it somehow...
}

在这很重要的情况下——例如,您想向用户弹出一个有用的注释,说明他们不允许-1在输入表单中输入——您可能需要抛出一个自定义异常,以便您可以更轻松地捕获后来:

try {
    baz();
    bar();
    foo(myInput);
} catch (BadUserInputException e) {
    reportError("Bad input: " + e.getMessage());
    log.info("recorded bad user input", e);
}

至于checkState,对我来说听起来不太对劲。该异常通常意味着问题出在所处的状态this(或应用程序中的某个其他更全局的状态)。从文档

表示方法已在非法或不适当的时间被调用。

在您的情况下, -1 永远不合适,因此checkState具有误导性。现在,如果它是:

if (x == -1 && (!allowNegativeOne()) { ... }

...那会更合适,尽管它仍然具有上述缺点IllegalArgumentException

所以,最后,有一个问题是你应该保持if原样,还是使用辅助方法。这真的归结为口味,检查的复杂程度以及使用频率(例如在其他方法中)。如果检查很简单,x == -1并且其他方法从未执行过检查(因此代码重用不是问题),我只会保留if.

于 2013-02-22T12:04:40.743 回答
2

如果该get方法只是将字符串转换为 int,那么它应该在那里进行验证,最好抛出非法ArgumentException 或一些这样的 RuntimeException。有了上述内容,您还可以在方法中混合抽象级别。例如,您checkNotNull抽象出param对 null 的检查,但param作为 int 的检查被拆分为get方法和foo方法。为什么没有一种 checkPreCondition 类型的方法?例如

private void paramShouldBeNonNullInt(String value) {
    if (value == null) throw new IllegalArgumentException("value was null");
    try {
       Integer.parseInt(value)
    } catch (NumberFormatException  e) {
        throw new IllegalArgumentException("value was not an integer");
    }
}
于 2013-02-22T11:41:44.423 回答
2

首先,您需要区分合同(例如断言/编程错误)和错误处理(例如可以并且应该被捕获和从中恢复的可恢复异常)。

如果您需要检查中间结果,似乎您不信任调用的服务,并且您希望确保您的假设成立。对?这应该表达为一个断言,而 Guava 对此没有很好的支持。

看看valid4j。在这里找到https://github.com/helsing/valid4j和在这里http://www.valid4j.org

然后我会表达这样的代码(使用valid4j对hamcrest-matchers的支持):

private int getFoo(String param) {
    require(param, notNullValue()); // Violation means programming error at client

    int x = get(param);

    ensure(x, not(equalTo(-1)); // Violation means programming error at supplier
    return x;
}
于 2014-12-06T11:07:44.277 回答
0

这里还有其他一些很好的答案。

从前提条件javadoc

前置条件异常用于发出调用方法出错的信号。(...) 后置条件或其他不变的失败不应该抛出这些类型的异常。

所以 ...

我应该将 if (...) {....} 部分包装在我自己的助手中吗?

不,现有的设施应该足够好。

或者我应该使用 Guava 的 checkState 吗?

是的,可能:如果需要在调用此方法之前从文件中加载参数,那么这将是必须如何使用此类的合同的一部分。

或者我应该将 get() 的失败视为 param 的结果并使用 checkArgument?

可能是:例如,如果对参数的语法有一些格式限制。(虽然也许那会进去get()

在这些情况下我应该使用断言吗?

是的。如果它不是像上面这样的前提条件检查,那么通常我会在assert这里使用。不要忘记您仍然可以添加消息:

assert x != 1 : "Indicates a bug.";

我发现这适合记录期望并验证类或方法的内部/私有实现。

If you want to make that a runtime check, you could do if (...) throw AssertionError but that's probably only necessary if you're working with dodgy code that you don't trust.

于 2015-06-11T22:51:57.313 回答