java中的断言编译为添加到测试中的私有合成静态布尔值 - 此处的提案很好地记录在案:
在其中,我们创建
final private static boolean $ assertionsEnabled = ClassLoader.desiredAssertionStatus(类名);
然后
assert(X)变为
if ($assertionsEnabled && !x) { throw }
这非常有意义;)
然而,我注意到我实际得到的是
public void test1(String s) {
assert (!s.equals("Fred"));
System.out.println(s);
}
变为
static final /* synthetic */ boolean $assertionsDisabled;
public void test1(String s) {
if ((!(AssertTest.$assertionsDisabled)) && (s.equals("Fred"))) {
throw new AssertionError();
}
System.out.println(s);
}
static {
AssertTest.$assertionsDisabled = !(AssertTest.class.desiredAssertionStatus());
}
我找不到任何关于他们为什么选择NEGATIVE测试而不是积极测试的文档 - 即原始提案捕获断言ENABLED,现在我们使用assertionsDISABLED。
我唯一可以想到的是,这可能(可能!)生成更好的分支预测,但这对我来说似乎是一个相当蹩脚的猜测 - Java哲学(几乎)总是使字节码变得简单,并且让JIT理清优化。
(请注意,这不是关于断言如何工作的问题 - 我知道!:))
(顺便说一下,看到这会导致错误的教程非常有趣!6.2.1 of this tutorial,有人引用回复a previous SO question on assertions会让测试错误!:)
有什么想法吗?
答案 0 :(得分:18)
这样做实际上是有原因的,而不仅仅是出于更紧凑的字节码或更快的条件执行之类的原因。如果您查看Java Language Specification, §14.10,将会看到以下注释:
启用在其类或接口完成初始化之前执行的assert语句。
还有一个包含初始化循环的示例:
class Bar {
static {
Baz.testAsserts();
// Will execute before Baz is initialized!
}
}
class Baz extends Bar {
static void testAsserts() {
boolean enabled = false;
assert enabled = true;
System.out.println("Asserts " +
(enabled ? "enabled" : "disabled"));
}
}
由于Bar
是Baz
的超类,因此必须在初始化Baz
之前对其进行初始化。但是,其初始化程序在尚未初始化的Baz
类的上下文中执行断言语句,因此没有机会设置$assertionsDisabled
字段。在这种情况下,该字段具有其默认值,并且所有操作均根据规范进行:断言已执行。如果我们有一个$assertionsEnabled
字段,则不会执行未初始化类的断言,因此会违反规范。
答案 1 :(得分:1)
布尔值实际上是用整数实现的。人们普遍认为与零比较更快,但我没有看到任何使用禁用而不是启用的理由。
恕我直言,因为false是布尔值的默认值,我尝试选择一个默认值为false
的标志。在这种情况下,$assertionsEnabled
会更有意义。
答案 2 :(得分:1)
虽然在查看反编译的java源代码时看起来有多余的工作要做 - 你不能依赖这个 - 你需要查看字节码级。
看看eclipse编译器和oracle javac产生的字节码:
#0: getstatic Test.$assertionsDisabled
#3: ifne #23
(assertion code) #6: aload_1
(assertion code) #7: ldc "Fred"
(assertion code) #9: invokevirtual String.equals(Object)
(assertion code) #12: ifeq #23
(assertion code) #15: new AssertionError
(assertion code) #18: dup
(assertion code) #19: invokespecial AssertionError.<init>()
(assertion code) #22: athrow
#23: getstatic System.out (PrintStream)
#26: aload_1
#27: invokevirtual PrintStream.println(String)
#30: return
请注意字节代码#3 - 它不需要反转Test.$assertionsDisabled
值,它只需要执行一次负面测试(即它不需要&#39;如果它是字节代码级别的否定测试或肯定测试,则会有所不同)
总之,它正在有效地实施,并且没有执行任何冗余工作。