[java] 讓構造函數拋出異常是一個好習慣嗎?



Answers

我一直認為在構造函數中拋出檢查異常是不好的習慣,或者至少應該避免的東西。

原因是你不能這樣做:

private SomeObject foo = new SomeObject();

相反,你必須這樣做:

private SomeObject foo;
public MyObject() {
    try {
        foo = new SomeObject()
    } Catch(PointlessCheckedException e) {
       throw new RuntimeException("ahhg",e);
    }
}

在構建SomeObject的時候,我知道它的參數是什麼,為什麼我需要將它包裝在try catch中呢? 啊,你說,但如果我從動態參數構造一個對象,我不知道它們是否有效。 那麼,你可以......在將參數傳遞給構造函數之前驗證這些參數。 這將是一個很好的做法。 如果你關心的是參數是否有效,那麼你可以使用IllegalArgumentException。

所以,而不是拋出檢查異常只是做

public SomeObject(final String param) {
    if (param==null) throw new NullPointerException("please stop");
    if (param.length()==0) throw new IllegalArgumentException("no really, please stop");
}

當然,有些情況下拋出一個檢查過的異常可能是合理的

public SomeObject() {
    if (todayIsWednesday) throw new YouKnowYouCannotDoThisOnAWednesday();
}

但多久可能呢?

Question

讓構造函數拋出異常是一個好習慣嗎? 例如,我有一個班級Person ,我有age作為其唯一屬性。 現在我提供這個課程

class Person{
  int age;
  Person(int age) throws Exception{
   if (age<0)
       throw new Exception("invalid age");
   this.age = age;
  }

  public void setAge(int age) throws Exception{
  if (age<0)
       throw new Exception("invalid age");
   this.age = age;
  }
}



我不是在構造函數中拋出異常,因為我認為這是非干淨的。 我的意見有幾個原因。

  1. 正如Richard所說,你不能以簡單的方式初始化一個實例。 特別是在測試中,僅僅通過在初始化過程中圍繞try-catch來構建一個全測試對像是非常煩人的。

  2. 構造函數應該是無邏輯的。 沒有任何理由將邏輯封裝在構造函數中,因為您始終致力於分離關注點和單一責任原則。 由於構造函數的關注點是“構造對象”,因此如果遵循這種方法,它不應該封裝任何異常處理。

  3. 它聞起來很糟糕的設計。 Imho如果我被迫在構造函數中做異常處理我首先問我自己是否在我的課上有任何設計欺詐。 有時候這是必要的,但是我把它外包給建造者或工廠,以使構造函數盡可能簡單。

所以如果有必要在構造函數中做一些異常處理,為什麼不把這個邏輯外包給一個Factory of Builder? 它可能還有更多的代碼行,但是可以讓你自由地實現一個更加健壯和適合的異常處理,因為你可以將異常處理的邏輯外包得更多,而且不會粘到構造函數上,這會封裝得太多邏輯。 如果您正確委派異常處理,客戶端不需要知道任何有關構建邏輯的知識。




我從來沒有認為在構造函數中拋出異常是一種不好的做法。 當班級設計完成後,你應該有一定的想法,記住該班級的結構應該是什麼。 如果其他人有不同的想法並嘗試執行該想法,那麼您應該相應地做出錯誤,並向用戶反饋錯誤是什麼。 就你而言,你可能會考慮類似的東西

if (age < 0) throw new NegativeAgeException("The person you attempted " +
                       "to construct must be given a positive age.");

NegativeAgeException是您自己構造的異常類,可能會擴展另一個異常,如IndexOutOfBoundsException或類似的東西。

斷言看起來並不是要走的路,因為你並沒有試圖發現代碼中的錯誤。 我會說,以異常終止絕對是在這裡做的正確的事情。




你不需要拋出一個檢查的異常。 這是程序控制中的一個錯誤,所以你想拋出一個未經檢查的異常。 使用Java語言已經提供的未經檢查的異常之一,例如IllegalArgumentExceptionIllegalStateExceptionNullPointerException

你也可能想擺脫二傳手。 您已經提供了一種通過構造函數啟動age的方法。 實例化後是否需要更新? 如果不是,則跳過setter。 一個很好的規則,不要讓事情比必要的更公開。 從私人或默認開始,並使用final保護您的數據。 現在每個人都知道Person已經被正確構建,並且是不可改變的。 它可以放心使用。

很可能這是你真正需要的:

class Person { 

  private final int age;   

  Person(int age) {    

    if (age < 0) 
       throw new IllegalArgumentException("age less than zero: " + age); 

    this.age = age;   
  }

  // setter removed



Links