为什么要用断言异常¶
C/C++和Python里虽然断言默认是打开、支持的,但我很少用过。而Java里更是默认不使用断言异常。就像上面说的,可以用if或try...catch 来代替断言。断言可有两个缺点,一是影响性能,二是对用户不友好,你看,一旦触发断言,程序90%跟你说不玩了,这太不友好了。
先列Zeller教授举的断言三优点:
- 程序及时退出,哪怕只是小错误而退出,也比坏数据好,因为坏数据不知道会造成什么样的后果。很多人说的史上最昂贵的bug,阿丽亚纳五号运载火箭,就因为惯性制导系统无法将一个64位的数据转换到16位格式而烧掉了5亿美元。
Zeller教授说,本来开发时是有对格式转换进行检查的断言的,结果产品中因为考虑性能问题,能关掉了这些断言。另外即使断言被触发,这火箭的软件系统也有足够优秀的异常处理机制来继续工作。所以可能就因为节省了这么一些断言,而创造了世上最奢侈的烟花。 - 断言可以让调试更简单。理由之前说过,更早地发现错误,更少的bug位置。
- 方便回溯缺陷。从出错断言开始,往回找,和2差不多的。
不过,Zeller教授也承认可以关闭一些影响性能的断言。
拿红黑树来说,没必要在一次时间复杂度O(log n) 的插入操作的前面,进行时间复杂度为O(n)的遍历操作,就为了检查是否存在环这一性质。这样的断言没必要。
个人理解¶
再来说说我的理解。
首先是为什么if和try...catch不能代替断言,也就是为什么还要用断言,这么一个不友好的语句。
个人理解,因为一个最简单的断言函数大概就是,如果断言条件为假,则抛出异常。其实也就是一个if语句能完成的事。但是一个简单的if语句抛出的异常所能显示的信息,不如标准的断言异常的信息多。而一个assert(condition)在很多时候从便捷上、作用上都高于一个if (condition) throw XXX; 所以If 不能代替断言。
而try...catch的情况相似,除了像IO等操作可能已经有内置的异常机制,面对新的异常条件时,同样要用if(condition)throw 异常,或assert(condition)来抛出异常,方能catch到,用if的话,也就可能需要定义新的异常类型,更加不如断言来得方便。当然try...catch和断言功能上重叠得不多,更加没有取代的可能了。
try...catch更像是断言的母亲,在断言捣蛋、对用户不好的时候,她来揪着断言的小耳朵,给用户赔礼道歉。
所以开发时, 要适当地结合 assert 和 异常处理机制。