断言使代码更稳定
理解Java中的断言(Assertion)是如何帮助开发者创建更稳定、品质更好且易于除错的代码
也许你还没注意到,Sun Microsystems已经增加了一个新的关键字到Java语言。这个关键字assert是JSR41的成果,一个Java规格请求(Java Specification Request)最终将一个真正的断言加入了Java语言。因为新关键字assert同C/C++的使用习惯有很大的不同。你将会从深入了解这个Java新特性中受益。
Java断言提供了一个特殊的功能:在用户定义的boolean表达式结果为false时抛出一个error。更确切的说,当需要在一个值为false时中断当前操作的话,可以使用断言。例如,你要开发一个可以在产品部署时关闭的debug模式,那断言对你来说非常有用。
开启你的断言
在使用断言以前,你需要先开启断言功能,因为Java Runtime Environment(JRE)默认关闭断言功能。你可以使用标记(flag) –enableassertions(缩写 -ea)来开启断言功能。同样,你也可以使用标记 –disableassertions(缩写 -da)来关闭断言功能。你可以在两个标记上使用下列选项:
l 无参数 标记接受全部断言
l 包名(packageName) 标记只接受包名和其子包(subpackage)内的类
l … 标记接受当前目录的默认包(default package)内的类
l 类名(className) 标记只接受指定类
看这个命令行:
java –ea:foo.bar… -da:foo.bar.old Myclass
意思是开启对于包foo.bar和其子包的断言,包foo.bar.old除外。
如何、在何时使用断言
Java断言有两种形式,第一种是:
assert some_boolean_expression
它说明如何some_boolean_expression为false,一个AssertionError会被抛出。注意,这是一个error,不是exception。也就说,这是一个不可控制异常(unchecked exception)(了解更多的关于error和exception的区别,请阅读作者的另一篇文章“理解error和exception之间的区别”)。
另一个更复杂的形式提供了更多的功能:
assert some_boolean_expression :
some_non-void_expression
这个版本与上面那个相比更为有用。第二个表达式,some_non-void_expression将会在AssertionError出现的时候被传送至构造器。这样可以在发生断言失败时获得更多的细节信息。注意,第二个参数可以是任何对象或原始类型(包括null)。
Listing 1提供了一个简单的例子,它使用了第二种断言的形式。由于AssertionError是unchecked exception,所以不需要去捕获(处于演示的目的,在Listing 1中使用了捕获)。事实上,你可能倾向于不去捕获AssertionError,但是不推荐这样,因为那可能使你的系统进入不稳定状态。特别是那些基于Swing(Swing-based)的应用程序。
Listing 1.这个简单的例子示范如何使用断言来检测那些肯定会出现但可能并不是我们所需要的结果。
public class Assert
{
public static void main(String[] args)
{
try
{
doSomething();
}
catch(AssertionError error)
{
error.printStackTrace();
}
}
public static int doSomething()
{
int i = 2;
assert i != 2 : "i was 2 for some reason";
return i;
}
}
在有些情况下不应该使用断言。例如,看看下列代码:
assert isValid(myObject);
尽管这样看起来还不坏,糟糕的是,断言有可能被关闭,这可是默认的状态。那么方法isValid永远不会被执行。一般来说,动作(actions)(方法、操作等)不应该被作为assert的第一个参数所使用,除非有特别的需要,因为它们可能不会被执行。
你可能在想,如何才能写出可以检测断言是否被开启的程序。虽然这并不是个好主意,但它还是十分有用的。现在Sun提供了这个小技巧:
static {
boolean assertsEnabled = false;
// here's the trick
assert assertsEnabled = true;
if( !assertsEnabled )
throw new RuntimeException(
"Asserts must be enabled!");
}
你可以在任何使用断言的代码中加入这个代码块。如果断言功能被关闭,那assert不会被执行,异常会被抛出。
解决兼容性问题
Java断言是一个新的语言特性,而不是一个API的增强。在诸多版本中,断言是第一个值得注意的改变,它同时也带来了binary版本的兼容问题。开启了断言支持进行编译后的文件无法在旧版JRE中执行。为了使开发人员更放心的使用断言,Sun使用了一些小办法。JDK 1.4的默认编译模式并不支持断言,并且编译带有断言的代码时会产生下列内容:
warning: as of release 1.4, assert is a
keyword, and may not be used as an identifier
除此之外,所有使用关键字assert的代码在编译时都会产生编译错误。如果需要编译使用断言的代码,必须使用标记 –source 1.4。例如,使用下列代码编译Listing 1中的代码:
javac -source 1.4 Assert.java
Java的断言技巧使开发者可以使用高级技巧创建出更稳定、更优质的代码,就像Design by Constract中说的那样。你需要仔细考虑我所提到的问题,但你肯定同意,断言的确是一个受欢迎的新特性。
关于作者
Josh Street是Bank of America的设计师。可以通过rjstreet@computer.org与他联络。