有時候,需求或設計時就可確認,程式執行的某個時間點或某個情況下,一定是處於或不處於何種狀態,若不是,則是個嚴重錯誤,開發過程中發現這種嚴重錯誤,必須立即停止程式確認需求與設計。
程式執行的某個時間點或某個情況下,必然處於或不處於何種狀態,這是一種斷言(Assertion),例如某個時間點程式某個變數值一定是多少。斷言的結果一定是成立或不成立,預期結果與實際程式狀態相同時,斷言成立,否則斷言不成立。
Java在JDK 1.4之後提供assert
語法,有兩種使用的語法:
assert boolean_expression;
assert boolean_expression : detail_expression;
boolean_expression
若為true
,則什麼事都不會發生,如果為false
,則會發生java.lang.AssertionError
,此時若採取的是第二個語法,則會將detail_expression
的結果顯示出來,如果當中是個物件,則呼叫toString()
顯示文字描述結果。
一個使用斷言的時機是內部不變量(Internal invarant)判斷,也就是某時間點上斷言某變數必然是或不是某值,像是代表儲值卡的CashCard
物件在扣款成功後,餘額一定不能是負數:
...
public void charge(int money) throws BalanceNotEnoughException {
checkGreaterThanZero(money);
checkBalance(money);
this.balance -= money;
assert this.balance > -1; // 一定不能是負數
}
private void checkGreaterThanZero(int money) {
if(money < 0) {
throw new IllegalArgumentException("扣負數?這不是叫我儲值嗎?");
}
}
private void checkBalance(int money) throws BalanceNotEnoughException {
if(money > this.balance) {
throw new BalanceNotEnoughException("錢不夠啦!");
}
}
...
若粗體字的斷言不成立,表示charge()
流程一定有嚴重錯誤,開發過程中必須停下來檢查問題出在哪。
斷言功能是在JDK 1.4之後提供,由於使用assert作為關鍵字,為了避免JDK 1.3或更早版本程式使用assert
作為變數導致名稱衝突問題,預設上執行時不啟動斷言檢查。如果要在執行時啟動斷言檢查,可以在執行java
指令時,指定-enableassertions
或是-ea
引數。
另一個使用斷言的時機為控制流程不變量(Control flow invariant)判斷,例如:
...
public static void play(int action) {
switch(action) {
case Action.STOP:
out.println("播放停止動畫");
break;
case Action.RIGHT:
out.println("播放向右動畫");
break;
case Action.LEFT:
out.println("播放向左動畫");
break;
case Action.UP:
out.println("播放向上動畫");
break;
case Action.DOWN:
out.println("播放向下動畫");
break;
default:
assert false : "非定義的常數";
}
}
...
開發人員使用play()
時,一定要使用Action
定義的列舉常數,如果不是,就有可能執行到default
,若此情況發生視為開發時期的嚴重錯誤,所以直接assert false
,必然斷言失敗。
注意!斷言是判定程式中的某個執行點必然是或不是某個狀態,所以不能當作像if
之類的判斷式來使用,assert
不應當做程式執行流程的一部份。