Summary: 某一段代碼須要對程序狀態作出某種假設。以斷言明確這表現這種假設。java
動機: 經常會有這樣一段代碼:只有當某個條件爲真時,該段代碼才能正常運行。例如平方根計算只對正直才能進行,又例如某個對象可能假設其字段至少有一個不等於null。程序員
這樣的假設一般並無在代碼中明確表現出來,你必須閱讀整個算法才能看出。有時程序員會以註釋寫出這樣的假設。而咱們要介紹的是一種更好的技術:使用斷言明確標明這些假設。算法
斷言首先是一個條件表達式,應該老是爲真。若是它失敗,表示程序員犯了錯誤。所以斷言的失敗應該致使一個非受控異常(unchecked exception)。斷言絕對不能被系統的其餘部分使用。實際上,程序最後的成品每每將斷言通通刪除。所以標記「某些東西是個斷言」是很重要的。函數
斷言能夠做爲交流與調試的輔助。在交流的角度上,斷言能夠幫助程序閱讀者理解代碼所作的假設;在調試的角度上,斷言能夠在距離bug最近的地方抓住它們。spa
作法:指針
若是程序員不犯錯,斷言就應該不會對系統運行形成任何影響,因此加入斷言永遠不會影響程序的行爲。調試
若是你發現代碼假設某個條件始終爲真,就加入一個斷言明確說明這種狀況。code
à你能夠新建一個Assert類,用於處理各類狀況下的斷言。對象
注意,不要濫用斷言。請不要使用它來檢查「你認爲應該爲真」的條件,請只用它來檢查「必定必須爲真」的條件。濫用斷言可能會形成難以維護的重複邏輯。在一段邏輯中加入斷言是有好處的,由於它迫使你從新考慮這段代碼的約束條件。若是不知足這些約束條件,程序也能夠正常運行,斷言就不會帶給你任何幫助,只會把代碼變得混亂,而且有可能妨礙之後的修改。get
你應該經常問本身:若是斷言指示的約束條件不能知足,代碼是否仍能正常運行?若是能夠,就把斷言拿掉。
另外,還須要注意斷言中的重複代碼。它們合其餘任何地方的重複代碼同樣很差聞。你能夠大膽使用Extract Method去掉那些重複代碼。
範例:
下面是一個簡單的例子:開支限制。後勤部門的員工每月有固定的開支限額;業務部門的員工則按照項目的開支限額來控制本身的開支。一個員工可能沒有開支額度可用,也可能沒有參與項目,但二者總要有一個(不然就沒有經費可用了)。在開支限額相關程序中,上述假設老是成立的,所以:
class Employee... private static final double NULL_EXPENSE = -1.0 private doulbe _expenseLimit = NULL_EXPENSE; private Project _primaryProject; double getExpenseLimt(){ return (_expenseLimit != NULL_EXPENSE)? _expenseLimit : _primaryProject.getMemberExpenseLimit(); } boolean withinLimint(doulbe expenseAmount){ return (expenseAmount <= getExpenseLimit()); }
這段代碼包含了一個明顯假設:任何員工要麼參與某個項目,要麼有我的開支限額。咱們可使用斷言在代碼中更明確地指出這一點:
double getExpenseLimt(){ Assert.isTrue(_expenseLimit != NULL_EXPENSE || _primaryProject !=null); return (_expenseLimit != NULL_EXPENSE)? _expenseLimit : _primaryProject.getMemberExpenseLimit(); }
這條斷言不會改變程序的任何行爲。另外一方面,若是斷言中的條件不爲真,咱們就會受到一個運行期異常:也許是withinLimit()函數拋出一個空指針異常,也許是在Assert.isTrue()函數中拋出一個運行期異常。有時斷言能夠幫助程序員找到bug,由於它離出錯地點很近。可是,更多時候,斷言的價值在於:幫助程序員理解代碼正確運行的必要條件。