2010-05-03 77 views
25

我在解決問題的同時,最近在Java中發現了assert關鍵字。起初,我很興奮。一些有用的東西,我不知道!一種更有效的方式來檢查輸入參數的有效性!耶學習!Java斷言是否被破壞?

但後來我拿了仔細一看,和「鍛鍊」爲「扼殺出完全」由一個簡單的事實,我的熱情就沒有那麼多:你可以把斷言關閉*

這聽起來就像一場噩夢。如果我聲稱如果輸入listOfStuffnull,我不希望代碼繼續運行,那麼爲什麼我會希望該斷言被忽略?這聽起來像是如果我正在調試一段產品代碼,並懷疑listOfStuff可能錯誤地通過了null,但沒有看到任何日誌文件證據表明該斷言被觸發,我不能相信listOfStuff實際上發送了一個有效的值;我還必須說明斷言可能完全被關閉的可能性。

這個假設我是一個調試代碼的人。有些不熟悉斷言的人可能會看到並假設(相當合理),如果斷言消息沒有出現在日誌中,listOfStuff就不成問題。如果你的第一次遇到assert是瘋狂的,你會發現它可能完全被關閉嗎?畢竟,這不像是有一個命令行選項可以讓你禁用try/catch塊。

所有這一切使我對我的問題(這是一個問題,而不是誇誇其談的藉口我保證!):

我缺少什麼?

是否有一些細微差異讓Java的實現assert遠比我給它的功用更有用?在某些情況下,從命令行啓用/禁用它的能力實際上是非常有價值的嗎?當我設想在生產代碼中使用它代替if (listOfStuff == null) barf();等語句時,我是否會誤解它?

我只是覺得這裏有一些重要的東西,我沒有得到。

*好的,從技術上講,它們實際上是默認關閉的;你必須竭盡全力打開它們。但是,仍然可以完全敲除它們。


編輯:啓示要求,啓發收到。

assert首先是一個調試工具,這個概念對我來說意義重大。

我仍然認爲應該在生產環境中禁用非平凡私有方法的輸入檢查,因爲開發人員認爲不好的輸入是不可能的。根據我的經驗,成熟的生產代碼是一個瘋狂的,龐大的事物,多年來由具有不同程度技能的人開發,針對不同程度的理智的快速變化的要求。即使不好的投入是不可能的,從現在開始六個月的一段草率維護編碼可以改變這種狀況。 The link gustafc provided(!感謝)包括本爲例:

assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;

禁止生產這種簡單的檢查,在我看來是愚蠢樂觀。但是,這是編碼哲學的一個區別,而不是一個破碎的特徵。

另外,我可以肯定看到的是這樣的值:

assert reallyExpensiveSanityCheck(someObject) : someObject;

我要感謝大家誰花時間幫我明白這個功能;這是非常讚賞。

+10

這看起來很像一個咆哮,而不是一個問題。 – 2010-05-03 15:39:29

+3

你曾經在C或C++中使用過ASSERT嗎?你有沒有希望你可以打開或關閉他們的手指? – 2010-05-03 15:46:51

+1

@Joachim:我明白了,但是很認真,我在這裏尋找啓示。這個功能對我來說確實確實有些破裂 - 但這種感覺完全等同於說「我的代碼沒有看到什麼問題,因此編譯器必須被破壞!」,只是在更高的層面上。我真的覺得我錯過了一些東西。如果你能爲我提供一種方式來表達,爲什麼這個功能讓我覺得不如破碎,這不會引起你的咆哮,我正在傾聽。 – BlairHippo 2010-05-03 15:47:14

回答

21

assert是一個有用的作品合同設計。在這種情況下,斷言可用於:

  • 先決條件檢查。
  • 後續檢查。
  • 中間結果檢查。
  • 類不變檢查。

斷言可能是昂貴的評估(例如,類不變量,它必須在調用任何公共方法之前和之後舉行)。斷言通常只在調試版本中用於測試目的;你斷言一些不可能發生的事情 - 這些事情是有bug的同義詞。斷言根據自己的語義驗證您的代碼。

斷言不是輸入驗證機制。當輸入在生產環境中(即輸入輸出圖層)可能確實是正確或錯誤時,請使用其他方法,例如異常或舊的條件檢查。

0

斷言是指代碼中可能可恢復的問題,或作爲調試的輔助手段。對於更嚴重的錯誤,您應該使用更具破壞性的機制,例如停止程序。

它們也可用於在應用程序稍後在調試和測試場景中失敗之前捕獲不可恢復的錯誤,以幫助您縮小問題的範圍。部分原因是,完整性檢查不會降低生產中經過良好測試的代碼的性能。

此外,在某些情況下(如資源泄漏),情況可能並不理想,但停止該計劃的後果比繼續執行的後果更糟。

+0

我不同意應該將斷言用於預計在生產中發生的可恢復錯誤。這通常是檢查異常的用途。但是,在某些情況下,既不例外,也不適用斷言。 – 2010-05-03 16:00:17

4

我認爲它的使用方式被解釋和設想。

如果您確實想在您的實際生產代碼中添加支票,爲什麼不直接使用If或任何其他條件聲明?

那些已經存在的語言,斷言的想法只是讓開發人員添加斷言,只是他們並不真正期望這種情況發生。

例如,檢查一個對象爲null,假設開發者編寫了一個私有方法,並在兩個地方調用它(這不是理想的例子,但可能適用於私有方法),他知道他傳遞了一個非null對象,而不是添加不必要的檢查,因爲從今天開始你知道沒有方法對象將是null 但是如果某人明天使用null參數調用此方法,那麼在開發人員的單元測試中,由於存在斷言,最終的代碼你仍然不需要如果檢查。

+0

這使我更加相信單元測試的完整性(或存在!),而不是我認爲的明智之舉,但是,我看到了它的邏輯。 :-) 謝謝。 – BlairHippo 2010-05-03 17:14:00

4

我所見過的每種語言都帶有關閉它們的能力。當你寫一個斷言時,你應該認爲「這很愚蠢,宇宙中沒有辦法做到這一點」 - 如果你認爲這可能是錯誤的,那應該是錯誤檢查。如果發生了可怕的錯誤,斷言只是爲了在開發過程中幫助你;當您構建生產代碼時,您將禁用它們以節省時間並避免(希望)多餘的檢查

+1

我認爲這是哲學上的差異,絆倒了我。對我而言,檢查輸入參數的合理性並不是評論他們是否可能是錯的;這是說這種方法需要他們不會錯。當然,我可能認爲不好的投入是不可能的,但是我知道什麼?代碼基礎非常龐大,我對它的理解並不完整,並且在將來的某個時候,一些白癡(比如我知道)可能會再次導致錯誤的輸入。但話雖如此,我看到能夠打開或關閉昂貴支票的價值。 – BlairHippo 2010-05-03 17:18:46

+0

「它應該是一個錯誤檢查」:你的意思是,在程序輸入數據的錯誤,而不是檢查程序本身的錯誤? – Raedwald 2011-09-14 11:03:31

19

Java的斷言並非真正用於參數驗證 - 它是specifically stated that assertions are not to be used instead of dear old IllegalArgumentException(也不是它們如何用於C-ish語言)。他們更多的是用於內部驗證,讓您對代碼進行假設,從代碼中查看它並不明顯。

至於關閉它,你也可以用C(++)來做到這一點,只是如果某人有無斷言的構建,他們無法打開它。在Java中,您只需使用適當的VM參數重新啓動應用程序即可。

+1

這並不完全是鏈接所說的。它表示它們不能用於PUBLIC方法中的參數驗證 - 它明確給出了一個在PRIVATE方法中使用它們進行參數驗證的示例。並且對於不明顯的代碼做出假設,是不是典型的評論工作?雖然不知道C/C++,我絕對看到如何通過命令行切換打開它比完全重新編譯更具吸引力。 – BlairHippo 2010-05-03 16:34:16

+0

是的,你可以對某些私有方法使用斷言。但是在生產中不應該對這些私有方法進行無效調用(如果不能保證這一點,請使用異常或其他機制)。該頁面明確指出,斷言應該用於記錄假設,而不是評論。請參閱'i%3 == 2'示例。 – 2010-05-03 16:41:16

1

這並不直接回答你關於assert的問題,但我建議查看guava/google-collections中的Preconditions類。它可以讓你寫得真好這樣的東西(使用靜態進口):

// throw NPE if listOfStuff is null 
this.listOfStuff = checkNotNull(listOfStuff); 

// same, but the NPE will have "listOfStuff" as its message 
this.listOfStuff = checkNotNull(listOfStuff, "listOfStuff"); 

好像這樣的事情可能是你想要什麼(它不能被關閉)。

1

斷言不是供最終用戶看到的。它們是給程序員的,所以你可以確保代碼在正在開發的過程中做的是正確的事情。一旦測試完成,斷言通常會因性能原因而關閉。

如果您預計生產中會發生什麼不良情況,例如listOfStuff爲空,那麼您的代碼沒有經過足夠的測試,或者在讓代碼擁有它之前您沒有對輸入進行消毒。無論哪種方式,「如果(壞東西){拋出異常}」會更好。斷言用於測試/開發時間,不用於生產。

4

斷言是爲了確保您確信您的代碼完全符合要求。這是在產品開發階段的調試幫助,通常在代碼發佈時會被省略。

我錯過了什麼?

你沒有按照他們打算使用的方式使用斷言。你說「檢查輸入參數的有效性」 - 這正是你要做的事情而不是想驗證斷言。

這個想法是,如果斷言失敗,你100%在你的代碼中有一個錯誤。斷言通常用於早期識別錯誤,否則就會出現。

+1

「你100%在你的代碼中有一個錯誤」:在代碼本身或斷言檢查的代碼中。 – Raedwald 2011-09-14 11:07:33

3

這聽起來是正確的。斷言只是一個對調試代碼有用的工具 - 它們不應該一直打開,特別是在生產代碼中。

例如,在C或C++中,斷言在發佈版本中被禁用。

+1

...以及默認的Java。 – DJClayworth 2010-05-03 16:33:23

3

斷言對於代碼維護者來說確實是一個非常簡潔的文檔工具。

例如我可以寫:

FOO應非空和大於0

更大 或把這種成程序的主體:

assert foo != null; 
assert foo.value > 0; 

它們對於記錄私有/包私有方法來表示原始程序員不變量非常有價值。

爲了增加額外的好處,當子系統開始表現出片狀時,您可以打開斷言並立即添加額外的驗證。

2

如果斷言無法關閉,那麼他們爲什麼應該存在。

如果你想的Performa上輸入合法性檢查,你可以隨便寫

if (foobar<=0) throw new BadFoobarException(); 

或者彈出一個消息框,或無論是在上下文中。

斷言的整個觀點是,它們可以打開進行調試並關閉生產。

1

如果您願意在斷言失敗時向最終用戶支付1美元,請使用assert

斷言失敗應該表示程序中的設計錯誤。

一個斷言聲明我已經設計了程序,這樣我就知道並確保指定的謂詞總是成立。

一個斷言對於我的代碼的讀者是有用的,因爲他們看到(1)我願意在這個屬性上設置一些錢;和(2)在以前的處決和測試案件中,財產確實持有。

我的賭注假設我的代碼的客戶堅守規則,並遵守他和我約定的合同。該合同可以容忍(所有輸入值允許和檢查有效性)或要求(客戶端和我同意他將永遠不會提供某些輸入值[描述爲預置條件],並且他不希望我檢查這些值一遍又一遍地)。 如果客戶堅守規則,而我的主張仍然失敗,客戶有權獲得一些補償。