關於 assert


有時候,需求或設計時就可確認,程式執行的某個時間點或某個情況下,一定是處於或不處於何種狀態,若不是,則是個嚴重錯誤,開發過程中發現這種嚴重錯誤,必須立即停止程式確認需求與設計。

程式執行的某個時間點或某個情況下,必然處於或不處於何種狀態,這是一種斷言(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不應當做程式執行流程的一部份。