考慮一個Product具有quantity可以增加和減少由給定amount。數量不應該變成負數,如果它會發生,必須禁止操作并警告用戶。
public class Product{
private int quantity;
public Product() {
quantity = 10;
}
public void decreaseQuantity(int amount) {
int decreasedQuantity = quantity - amount;
if(decreasedQuantity < 0 )
throw new RuntimeException(String.format("Decrease quantity (%s) exceeds avaiable quantity (%s)",
amount, quantity));
quantity = decreasedQuantity;
}
}
例如,如果產品的數量為 10,而我嘗試洗掉 20,則會拋出 RuntimeException。SonarCloud 建議用自定義例外替換 RuntimeException,但我想知道是否有適合這種情況的標準例外(Effective Java:Favor The Use of Standard Exceptions)。
最合適的例外似乎是IllegalStateException. 來自 javadoc
表示在非法或不適當的時間呼叫了方法。換句話說,Java 環境或 Java 應用程式未處于所請求操作的適當狀態。
和來自 Effective Java
IllegalStateException:如果在呼叫方法時物件的狀態對該操作無效,則使用此例外。您可能有一個檔案句柄,并且在打開它之前呼叫了 read。
但是,在我看來,我的示例與檔案中假設的內容之間存在細微差別:不是物件本身的狀態使操作非法,而是物件的狀態和輸入的值范圍。閱讀使用示例(例如IllegalStateException 的預期用途是什么?),無論輸入引數如何,物件始終處于拒絕操作的狀態。
uj5u.com熱心網友回復:
核心 Java 庫中沒有任何東西是灌籃高手。但是,讓自己的例外可能是您最好的選擇,這與使用IllegalArgumentException.
RuntimeException
出于模糊的原因不建議。不使用它的主要原因是因為 linter 工具會沖你大喊大叫。他們對你大喊大叫的原因是雙重的,要確定正確的舉動是告訴 linters 閉嘴還是引導他們的建議,重點在于以下兩個原因:
例外的型別名稱本身就是資訊。例如,
throw new NullPointerException("x is null")撰寫愚蠢的代碼(即使它很常見)。這是多余的——恰到好處new NullPointerException("x")。RuntimeException幾乎不傳達任何資訊。您主要是在避免這個陷阱:雖然 RuntimeException 確實幾乎沒有傳達任何資訊,但例外的訊息說明了一切,因此,linter 試圖阻止發生的事情(拋出一個不能正確傳達問題本質的例外) ) 沒有發生,因此你應該考慮只告訴 linter 停止抱怨......除了:您想要一個正確的例外型別的第二個原因是您真的,真的不希望打算捕獲它的代碼必須對例外訊息進行字串分析。因此,如果在任何世界中,您可以預見某些代碼想要呼叫您的方法,然后以除炸毀所有背景關系之外的任何方式對條件(嘗試減去超過可用的數量)做出反應,那么您應該拋出一個例外意味著這個特定的條件,沒有別的。如果您的意圖是捕獲特定條件,
RuntimeException則永遠不適合捕獲(這幾乎永遠不合適,句號 - 有時您想運行代碼并對任何問題做出反應,而不管其性質如何,但隨后catch (Exception e)是適當的 catch 塊,甚至Throwable. 例如應用程式服務器等應該這樣做。
IllegalArgumentException
linters 不會大喊大叫,但您仍然沒有真正獲得次要好處(即,允許呼叫者捕獲這個特定問題,而不是引數的其他一些問題)。這在“精神上”也有點可疑:您最初無視它的理由并沒有錯。通常,IAE 被理解為意味著無論此物件的狀態如何,非法引數都是非法的。但這不是寫在 IAE 的 javadoc 中,也不是普遍適用的。
所以,缺點是:
- 強迫呼叫者
catch (IllegalArgumentException e)處理想要對減去的數量做出反應是有點推動的。IAE 還是太“籠統”了。 - 這可能有點令人困惑。
好處很簡單:
- 它已經在那里了。
IllegalStateException
我認為這比 IAE 更糟糕。這幾乎是相同的故事,除了這里的混淆是 ISE 通常用于標記物件的當前狀態,以至于您嘗試呼叫的操作根本不可用。以經過調整的方式實際上是正確的(物件的狀態是有 3 個專案;因此它現在不是處于decreaseQuantity(5)可用操作的狀態),但感覺比 IAE 更令人困惑:ISE 感覺產品物件本身是處于無效狀態。我假設該產品是一種遺留產品,現在沒有或將不會再有庫存,或者是某種虛擬產品型別(代表“未知產品”或類似異國情調的產品的 Product 物件)。
但是,與 IAE 的處理相同:ISE 的 javadoc 并沒有明確說明您不能這樣做。因此,如果你更愿意拋出這個,你可以,它不是可證明的不正確,最壞的情況是它只是糟糕的代碼風格。linter 工具永遠不會因此而責怪您,或者如果是,則 linter 工具是錯誤的。
寫你自己的
好處:
- 你的庫的用戶不可能帶來錯誤的假設(IAE 的意思是:無論狀態如何,引數都是非法的);這使它不那么令人困惑。
- 如果代碼打算呼叫您的方法并專門為洗掉超過存在的條件撰寫一個 catch 子句,那么這絕對是最好的答案。這:
try {
product.decreaseQuantity(5);
} catch (QuantityInsufficientException e) {
ui.showOrderingError("Regretfully we don't have this item in stock at the quantity you want");
}
比 更易讀catch (IllegalArgumentException),更重要的是,它更可靠:IAE 是一種經常使用的例外,以至于有一天您將編輯該decreaseQuantity方法并引入一個由于其他原因引發 IAE 的代碼路徑,現在您有一個很難找到錯誤。
結論
我會寫你自己的。是的,必須撰寫源檔案有點麻煩,但是您可以讓您的 IDE(或Project Lombok以實作最大的樣板破壞)生成整個檔案,并且您可能永遠不必InsufficientQuantityException.java再看一遍。
uj5u.com熱心網友回復:
java.lang.IllegalArgumentException 在這種情況下是正確的答案。
此示例等效于Effective Java - 第三版(第 301 頁)中的以下示例:
考慮一個代表一副牌的物件的情況,并假設有一種方法可以從一副牌中發一手牌,并將手牌的大小作為引數。如果呼叫者傳遞的值大于一副牌中剩余牌的數量,則可以將其解釋為 IllegalArgumentException(handSize 引數值太高)或 IllegalStateException(一副牌中的牌太少)。在這些情況下,規則是如果沒有引數值有效則拋出 IllegalStateException,否則拋出 IllegalArgumentException。
這IllegalStateException是不正確的,因為物件的狀態不會阻止呼叫decreaseQuantity:您可以呼叫它,只需使用適當的輸入值即可。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/394885.html
