似乎人们普遍认为断言语句应保留用于测试并在生产中禁用,因为错误应该已经解决,并且启用断言会影响性能。然而,使用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
我是否错过了任何令人信服的理由,如果条件优于断言,为什么会进行空检查?
答案 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)
根据我的经验,如果x
或y
为空,实际上是错误(而不是您的第一个示例),将编码的是:
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.
}
正如我在下面的一些讨论中所提到的,如果您事先知道某些参数值正在破坏(如浮点/双精度中的null
或NaN
)那么它会产生很多快速失败的感觉例如,它可以帮助调用者更好地隔离问题,而不是将null
值放入Map
并且NPE会被抛出到另一个线程上。
(注意:编辑以删除使用注释的有争议的建议。)