断言空检查

时间:2018-05-30 21:58:11

标签: java

似乎人们普遍认为断言语句应保留用于测试并在生产中禁用,因为错误应该已经解决,并且启用断言会影响性能。然而,使用if语句进行空检查肯定也是如此。为什么这段代码被认为适合生产

> do.call(rbind, lapply(mapply(rep, A$Amount, A$Times), `length<-`, max(A$Times)))
     [,1] [,2] [,3] [,4]
[1,]   50   50   50   50
[2,]   80   80   NA   NA

但这段代码不是吗? (假设启用了断言)

if(x != null) {
    x.setId(idx);
    if (y != null) {
        if (y.id == x.id) {
            x.doSth();
        }
    } else {
        //handle error
    }
} else {
    //handle error
}

我理解在预期变量可能未初始化时使用if语句。然而,当它用于防御性编码时,断言似乎更优雅和可读。

我还测试了每种方法的性能:

try {
    assert(x != null); 
    x.setId(idx);
    assert(y != null);
    if (y.id == x.id) {
        x.doSth();
    }
} catch (AssertionError e) {
    //handle error
}

两种方法的时间安排大致相同:

public class AssertTest {

    static final int LOOPS = 10000000;

    public static void main(String[] args) {

            String testStr = "";

            long startNotEqualsTest = System.currentTimeMillis();
            for (int i = 0; i < LOOPS; i++) {
                if (testStr != null) {
                    testStr = System.currentTimeMillis() + "";
                } else {
                    throw new RuntimeException("We're doomed");
                }
            }
            long notEqualsTestDuration = System.currentTimeMillis() - startNotEqualsTest;

            testStr = "";

            long startAssertTest = System.currentTimeMillis();      
            for (int i = 0; i < LOOPS; i++) {
                try {
                    assert(testStr != null);
                    testStr = System.currentTimeMillis() + "";
                } catch (AssertionError e) {
                    throw new RuntimeException("We're doomed");
                }
            }
            long assertTestDuration = System.currentTimeMillis() - startAssertTest;

            System.out.println("Duration with if : " + notEqualsTestDuration + "ms");
            System.out.println("Duration with assert : " + assertTestDuration + "ms");  
    }
}

禁用断言几乎没有区别:

Duration with if : 1234ms
Duration with assert : 1313ms

我是否错过了任何令人信服的理由,如果条件优于断言,为什么会进行空检查?

3 个答案:

答案 0 :(得分:5)

问问自己 如何处理null案件?如果对象是NullPointerException不应该做什么,你会怎么做在大多数情况下,完全没有处理它。让它在执行后的某个时间产生ExceptionHandler。无论如何,它很可能是一个编程错误,因此null最终将其写入日志会被捕获。

当然,有些情况下您需要对if-else个对象做出反应。 assert是针对此类案件而制作的。它很容易,自我解释,每个程序员都知道构造,所以为什么不使用它。无论如何,不​​鼓励生产Validate.notNull(myObj) Validate.hasEmailCorrectSyntax("foo@bar.com"); 。例如,请参阅SO上的this post。而且,根据我的口味,使用try / catch块非常麻烦。

还有其他选择。例如,DavidW建议使用注释,这是完全没问题的,只要您确保解释这些注释(例如,在迁移代码时可能会出现问题)。

另一种方法是 Validator 类。例如, Apache Commons 库有一个Validate类,用于检查某些条件,如果条件未满,则会抛出相应的异常。当然,您也可以编写自己的 Validators ,它们也会抛出您的自定义异常。你最终会得到一个简洁的单行内容,如

{{1}}

另请参阅Java Object.requireNotNull

答案 1 :(得分:2)

完全使用assert必须假定-ea标志已启用,并且在许多情况下......它不是。 assert将完成与空检查相同的操作,明显的副作用是,如果没有以特定方式配置其环境,则代码将无法按预期运行。

出于这个原因,避免assert并在必要时利用空检查是有意义的。更简洁的是,如果一个可以,最好在Optional实例中包装可能为null的所有内容,并使用ifPresent(或{{1}对其进行操作因为你似乎想要指出一个错误,如果这些值为null)。

答案 2 :(得分:0)

根据我的经验,如果xy为空,实际上是错误(而不是您的第一个示例),将编码的是:

void someFunction(AnObject x, AnObject y) {
    if (x == null || y == null) {
        throw new IllegalStateException("Contract violation: x and y must be non-null");
    }
    // rest of method can be run without null checks.
}

正如我在下面的一些讨论中所提到的,如果您事先知道某些参数值正在破坏(如浮点/双精度中的nullNaN)那么它会产生很多快速失败的感觉例如,它可以帮助调用者更好地隔离问题,而不是将null值放入Map并且NPE会被抛出到另一个线程上。

(注意:编辑以删除使用注释的有争议的建议。)