是否有一种美妙的方式来断言Java方法中的前提条件?

时间:2011-07-25 12:44:01

标签: java contract eiffel

我的很多函数都在声明之下有一整堆验证代码:

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

我想精确地指定某些输入的有效范围 - 例如A&gt; B,C =&gt;  1或str_d.length()&gt; 0.

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

在我的方法中是否有更优雅的方法来指定前置条件,后置条件(以及可能的不变条件)。

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

8 个答案:

答案 0 :(得分:12)

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

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

它还可以轻松地向邮件添加更多信息,如果支票通过,则无需支付String连接费用。

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

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

答案 1 :(得分:6)

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

以下是tutorial的代码段:

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);
}

答案 2 :(得分:5)

assert start < end怎么样?看看documentation

答案 3 :(得分:5)

Aspect oriented programming可用于此类问题。可以拦截方法调用以检查不变量。切入点和建议以声明方式配置。 SpringGuice直截了当地使用了AOP。

这是一个example in Guice

答案 4 :(得分:4)

您可以使用注释和面向方面的编程来完成此操作。

如果参数组合不合法,我会使用IllegalArgumentException。我会使用IllegalStateException处于阻止该方法工作的状态。

您可以为异常创建辅助方法。

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

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

答案 5 :(得分:1)

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

请注意,应始终启用输入验证。因此,它在概念上与断言检查非常不同(例如,在Eiffel中),可以选择性地打开/关闭 - 查看相关堆栈溢出问题的答案When should I use Apache Commons' Validate.isTrue, and when should I just use the 'assert' keyword?

答案 6 :(得分:1)

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

答案 7 :(得分:-2)

JUnit包具有类似assert的结构,可以帮助进行这样的条件检查。