12

我的许多函数在声明下方都有大量验证代码:

if ( ! (start < end) ) {
    throw new IllegalStateException( "Start must be before end." );
    }

我想精确指定某些输入的有效范围 - 例如 A > B、C => 1 或 str_d.length() > 0。

鉴于我的一些函数有很多必须验证的参数,我最终可以编写很多样板来验证前置条件。我正在编写一个主要供非技术开发人员使用的库,我们发现验证函数输入是帮助用户正确操作 API 的最佳方式。我们越早提出错误,客户要做的工作就越少。

有没有更优雅的方法来指定我的方法中的前置条件、后置条件(可能还有不变条件)。

一位同事告诉我 Eiffel 编程语言的一个特性,它允许以非常自然的方式描述前/后/不变条件,而无需重复大量样板代码。Java 语言是否有一个附加组件可以让我使用其中的一些魔法?

4

8 回答 8

13

GuavaPreconditions类就是为了这个。您通常将它与静态导入一起使用,因此您的示例如下所示:

checkArgument(start < end, "Start must be before end");

它还可以轻松地向消息添加更多信息,String如果检查通过,则无需支付连接成本。

checkArgument(start < end, "Start (%s) must be before end (%s)", start, end);

assert语句不同,这些不能被禁用。

于 2011-07-25T12:56:34.370 回答
6

查看Cofoja项目,该项目通过注释为 Java 提供合同。它提供了前置/后置条件和不变量。此外,与其他 Java 实现相比,它正确处理父类/接口中定义的合同。合同评估可以在运行时启用/禁用。

这是他们教程中的代码片段:

import com.google.java.contract.Invariant;
import com.google.java.contract.Requires;

@Invariant("size() >= 0")
interface Stack<T> {
  public int size();

  @Requires("size() >= 1")
  public T pop();

  public void push(T obj);
}
于 2011-07-25T23:10:04.270 回答
5

怎么样assert start < end。看看文档

于 2011-07-25T12:47:58.220 回答
4

Aspect oriented programming can be used for such a problem. Method calls can be intercepted checking for the invariant. Pointcuts and advices are configured in a declarative way. Spring and Guice make usage of AOP straightforward.

Here's an example in Guice.

于 2011-07-25T12:51:01.680 回答
4

You might be able to do this with annotation and aspect orientated programming.

I would use IllegalArgumentException if an argument combination is not legal. I would use IllegalStateException is in a state which prevents the method from working.

You can create a helper method for the exception.

public static void check(boolean test, String message) {
    if(!test) throw new IllegalArgumentException(message);
}

check(start < end, "Start must be before end.");
于 2011-07-25T12:51:39.563 回答
1

如果我发现自己在一个类中重复了相同的样板前置条件检查代码,我会重构我的代码以减少重复并通过将重复的代码提取到新的 ( static private) 方法中来增加抽象。我使用 Java-7Objects.requireNonNull方法进行null检查。

于 2014-11-28T08:16:08.173 回答
1

对于输入验证,您还可以使用Apache Commons Validator

请注意,应始终启用输入验证。因此,它在概念上与断言检查(例如,在 Eiffel 中)非常不同,断言检查可以选择打开/关闭 - 请参阅此相关堆栈溢出问题的答案我何时应该使用 Apache Commons 的 Validate.isTrue,以及何时应该我只是使用'assert'关键字?

于 2012-04-26T20:32:39.623 回答
-2

The JUnit package has constructs like assert that would aid in doing such condition check.

于 2011-07-25T12:51:00.450 回答