java group解決 exception中文 - 避免!= null語句




15 Answers

這對我來說聽起來像是一個相當普遍的問題,初級到中級開發人員往往會在某些方面面臨這樣的問題:他們要么不知道,要么不信任他們參與的合同,並且防禦性地過度檢查空值。 另外,在編寫自己的代碼時,它們傾向於依賴返回空值來指示某些東西,從而要求調用者檢查空值。

換句話說,有兩個實例進行空檢查:

  1. 如果null是合同方面的有效回复; 和

  2. 哪裡不是有效的回复。

(2)很容易。 使用assert語句(斷言)或允許失敗(例如, NullPointerException )。 斷言是1.4中添加的高度未充分利用的Java功能。 語法是:

assert <condition>

要么

assert <condition> : <object>

其中<condition>是一個布爾表達式, <object>是一個對象,其toString()方法的輸出將包含在錯誤中。

如果條件不為真,則assert語句拋出ErrorAssertionError )。 默認情況下,Java會忽略斷言。 您可以通過將選項-ea傳遞給JVM來啟用斷言。 您可以為各個類和包啟用和禁用斷言。 這意味著您可以在開發和測試時使用斷言驗證代碼,並在生產環境中禁用它們,儘管我的測試表明,斷言沒有性能影響。

在這種情況下不使用斷言是可以的,因為代碼只會失敗,如果使用斷言將會發生這種情況。 唯一的區別是,斷言可能會更快地發生,以更有意義的方式發生,並且可能還有額外的信息,這可能會幫助您弄清楚如果您不期望它發生的原因。

(1)有點難。 如果您無法控制您正在調用的代碼,那麼您就會陷入困境。 如果null是有效響應,則必須檢查它。

如果它是你控制的代碼(然而通常就是這種情況),那麼這是一個不同的故事。 避免使用空值作為響應。 使用返回集合的方法,很容易:幾乎一直返回空集合(或數組)而不是null。

對於非收藏,它可能更難。 以此為例:如果您有這些接口:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

Parser採用原始用戶輸入並找到要做的事情,也許是在為某些事情實現命令行界面時。 現在,如果沒有適當的操作,您可以使合同返回null。 這導致你正在談論的空檢查。

另一種解決方案是永遠不會返回null,而是使用Null Object模式

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

相比:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

ParserFactory.getParser().findAction(someInput).doSomething();

這是一個更好的設計,因為它導致更簡潔的代碼。

也就是說,也許findAction()方法完全適合拋出帶有意義錯誤消息的異常 - 特別是在你依賴用戶輸入的情況下。 對於findAction方法來說,拋出一個Exception比使用一個簡單的NullPointerException而沒有解釋的調用方法要好得多。

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

或者,如果您認為try / catch機制太難看,而不是Do Nothing,則默認操作應該向用戶提供反饋。

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}
nullpointerexception意思 nullpointerexception原因

我使用object != null來避免NullPointerException

有沒有一個很好的替代品呢?

例如:

if (someobject != null) {
    someobject.doCalc();
}

當不知道對像是否為null時,這可以避免NullPointerException

請注意,接受的答案可能已過期,請參閱https://.com/a/2386013/12943以獲取更新的方法。




如果不允許空值

如果您的方法是外部調用的,請從以下內容開始:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

然後,在該方法的其餘部分中,您將知道該對object不為null。

如果它是一個內部方法(不是API的一部分),只需記錄它不能為null,就是這樣。

例:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

但是,如果您的方法只是傳遞了值,並且下一個方法將其傳遞給它,則可能會出現問題。 在這種情況下,您可能需要檢查上面的參數。

如果允許null

這真的取決於。 如果發現我經常做這樣的事情:

if (object == null) {
  // something
} else {
  // something else
}

所以我分支,做兩件完全不同的事情。 沒有醜陋的代碼片段,因為我真的需要根據數據做兩件事。 例如,我應該處理輸入,還是應該計算一個好的默認值?

我很少使用成語“ if (object != null && ... ”)。

如果您顯示通常使用成語的示例,則可能更容易為您提供示例。




如果不允許使用未定義的值:

您可以配置IDE以警告您可能存在空取消引用。 例如,在Eclipse中,請參閱首選項> Java>編譯器>錯誤/警告/空分析

如果允許未定義的值:

如果要定義未定義值有意義的新API ,請使用選項模式 (可能熟悉函數式語言)。 它具有以下優點:

  • API中明確說明輸入或輸出是否存在。
  • 編譯器強制您處理“未定義”的情況。
  • Option是monad ,因此不需要詳細的null檢查,只需使用map / foreach / getOrElse或類似的組合器來安全地使用該值(example)

Java 8有一個內置的Optional類(推薦); 對於早期版本,有庫替代品,例如GuavaOptionalFunctionalJavaOption 。 但是與許多功能樣式模式一樣,在Java中使用Option(甚至8)會產生相當多的樣板,使用較簡潔的JVM語言(例如Scala或Xtend)可以減少這種樣板。

如果你必須處理可能返回null的API ,那麼你在Java中做不了多少。 Xtend和Groovy有Elvis運算符 ?:null-safe dereference運算符 ?. ,但請注意,如果是空引用,則返回null,因此它只是“延遲”對null的正確處理。




使用Java 8,新的java.util.Optional類可以解決一些問題。 至少可以說它提高了代碼的可讀性,在公共API的情況下,使API的合同更清晰。

他們的工作方式如下:

創建給定類型( Fruit )的可選對像作為方法的返回類型。 它可以為空或包含Fruit對象:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

現在看一下我們搜索給定Fruit實例的Fruitfruits )列表的代碼:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

您可以使用map()運算符對可選對象執行計算或從中提取值。 orElse()允許您為缺失值提供回退。

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

當然,檢查null / empty值仍然是必要的,但至少開發人員意識到該值可能是空的並且忘記檢查的風險是有限的。

在每次返回值可能為空時使用Optional構建的API中,只有當它不能為null (約定)時才返回普通對象,客戶端代碼可能會放棄對簡單對象返回值的空檢查...

當然Optional也可以用作方法參數,在某些情況下,可能是指示可選參數而不是5或10重載方法的更好方法。

Optional提供了其他方便的方法,例如允許使用默認值的ifPresent ,以及使用lambda表達式的 ifPresent

我邀請您閱讀本文(我寫這個答案的主要來源),其中NullPointerException (以及一般的空指針)有問題以及Optional帶來的(部分)解決方案得到了很好的解釋: Java可選對象




  • 如果你認為一個對像不應該為null(或者它是一個bug),請使用一個斷言。
  • 如果你的方法不接受null params在javadoc中說它並使用斷言。

只有當你想處理對象可能為null的情況時,你必須檢查對象!= null ...

有人建議在Java7中添加新的註釋來幫助null / notnull params: http://tech.puredanger.com/java7/#jsr308 ://tech.puredanger.com/java7/#jsr308




Google集合框架提供了一種實現空檢查的優雅方式。

像這樣的庫類中有一個方法:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

用法是(使用import static ):

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

或者在你的例子中:

checkNotNull(someobject).doCalc();



而不是Null對像模式 - 它有其用途 - 你可能會考慮null對像是一個bug的情況。

拋出異常時,檢查堆棧跟踪並完成錯誤。




Null不是'問題'。它是complete建模工具集的組成部分。軟件旨在模擬世界的複雜性,而null則承擔其負擔。Null表示 Java等中的“無數據”或“未知”。因此,為這些目的使用null是合適的。我不喜歡'Null對象'模式;我認為它會提升誰來守護守護者 ”的問題。
如果你問我女朋友的名字我會告訴你我沒有女朋友。在Java語言中,我將返回null。另一種方法是拋出有意義的例外來表明一些不可能的問題(或者不要我希望在那裡解決並將其委託給堆棧中較高的位置,以重試或向用戶報告數據訪問錯誤。

  1. 對於“未知問題”,請給出“未知答案”。(從業務角度來看,這是正確的,這是正確的)在使用之前在方法內檢查一次null參數會減少多個調用者在調用之前檢查它們。

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }
    

    上一步導致正常的邏輯流程,從我的照片庫中得不到任何不存在的女朋友的照片。

    getPhotoOfThePerson(me.getGirlfriend())
    

    它適合新的Java API(期待)

    getPhotoByName(me.getGirlfriend()?.getName())
    

    雖然對於某些人來說,不是為了找到存儲在數據庫中的照片而是“正常的業務流程”,但我過去常常使用如下所示的對

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);
    

    並且不要厭惡鍵入<alt> + <shift> + <j>(在Eclipse中生成javadoc)並為您的公共API編寫三個額外的單詞。對於那些沒有閱讀文檔的人來說,這將是綽綽有餘的。

    /**
     * @return photo or null
     */
    

    要么

    /**
     * @return photo, never null
     */
    
  2. 這是理論上的情況,在大多數情況下,您應該更喜歡java null safe API(如果它將在另外10年內發布),但它NullPointerException是一個的子類Exception因此,它Throwable表示合理的應用程序可能想要捕獲的條件(javadoc)!為了使用異常的第一個最大優勢和從“常規”代碼中分離錯誤處理代碼(根據Java的創建者),對我來說,抓住它是合適的NullPointerException

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }
    

    可能會出現問題:

    問:如果getPhotoDataSource()返回null會怎麼樣?
    答:這取決於業務邏輯。如果我找不到相冊,我會告訴你沒有照片。如果未初始化appContext怎麼辦?這種方法的業務邏輯就是這樣。如果相同的邏輯應該更嚴格,那麼拋出異常它就是業務邏輯的一部分,應該使用null的顯式檢查(情況3)。在新的Java空安全的API更適合的位置,指定選擇性意味著什麼,什麼不意味著被初始化是快速失敗的編程錯誤的情況下。

    問:可以執行冗餘代碼並且可以獲取不必要的資源。
    答:如果getPhotoByName()嘗試打開數據庫連接,最後創建PreparedStatement並使用人名作為SQL參數,則可能會發生這種情況。針對未知問題的方法給出了未知答案(案例1)。在獲取資源之前,該方法應檢查參數並在需要時返回“未知”結果。

    問:由於嘗試關閉開放,這種方法會帶來性能損失。
    答:首先應該易於理解和修改軟件。只有在此之後,才能考慮性能,只有在需要時!並在需要的地方!(source)和許多其他人)。

    PS。這種方法使用起來也是合理的,因為“常規”代碼原則的單獨錯誤處理代碼在某些地方使用是合理的。考慮下一個例子:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }
    

    PPS。對於那些快速下載(並且讀取文檔不那麼快)的人,我想說我的生活中從未發現過空指針異常(NPE)。但這種可能性是由Java創建者故意設計的,因為NPE是其子類Exception。我們在Java歷史上有一個先例,當時ThreadDeathError不是因為它實際上是一個應用程序錯誤,而僅僅是因為它不打算被抓住!多少NPE適合成為一個ErrorThreadDeath!但事實並非如此。

  3. 僅在業務邏輯暗示時才檢查“無數據”。

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }
    

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }
    

    如果未初始化appContext或dataSource,則未處理的運行時NullPointerException將Thread.defaultUncaughtExceptionHandler當前線程並將由Thread.defaultUncaughtExceptionHandler處理(您可以定義和使用您喜歡的記錄器或其他通知機制)。如果未設置,ThreadGroup#uncaughtException將stacktrace打印到系統錯誤。應該監視應用程序錯誤日誌並為每個未處理的異常打開Jira問題,這實際上是應用程序錯誤。程序員應該在初始化的東西中修復bug。




Java中常見的“問題”確實存在。

首先,我對此的看法:

我認為在傳遞NULL時“吃”某些東西是不好的,其中NULL不是有效值。如果你沒有以某種錯誤退出該方法,那麼這意味著你的方法沒有出錯,這是不正確的。然後你可能在這種情況下返回null,並且在接收方法中你再次檢查null,它永遠不會結束,你最終得到“if!= null”等。

因此,IMHO,null必須是一個嚴重的錯誤,它會阻止進一步執行(即null不是有效值)。

我解決這個問題的方法是這樣的:

首先,我遵循這個慣例:

  1. 所有公共方法/ API總是檢查其參數是否為null
  2. 所有私有方法都不檢查null,因為它們是受控方法(如果上面沒有處理的話,只需使用nullpointer異常)
  3. 唯一不檢查null的其他方法是實用程序方法。它們是公開的,但如果你出於某種原因打電話給它們,你就知道你傳遞了什麼參數。這就像試圖在沒有提供水的情況下在水壺中煮水...

最後,在代碼中,public方法的第一行如下:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

請注意,addParam()返回self,因此您可以添加更多參數進行檢查。

如果任何參數為null,validate()則拋出方法將被檢查ValidationException(選中或取消選中更多是設計/品味問題,但我ValidationException的選中)。

void validate() throws ValidationException;

例如,如果“plans”為null,則該消息將包含以下文本:

參數[plans]遇到非法參數值null

正如您所看到的,用戶消息需要addParam()方法(字符串)中的第二個值,因為您無法輕鬆檢測傳入的變量名稱,即使使用反射(無論如何都不是此帖子的主題......)。

是的,我們知道超出這一行我們將不再遇到空值,所以我們只是安全地調用這些對像上的方法。

這樣,代碼就乾淨,易於維護和讀取。




除了使用之外,assert您還可以使用以下內容:

if (someobject == null) {
    // Handle null here then move on.
}

這略好於:

if (someobject != null) {
    .....
    .....



    .....
}



Guava是Google非常有用的核心庫,它有一個很好的有用API來避免空值。我發現Guava非常有幫助。

如維基中所述:

Optional<T>是一種用非空值替換可空T引用的方法。Optional可以包含非空T引用(在這種情況下我們說引用是“存在”),或者它可以不包含任何東西(在這種情況下我們說引用是“不存在”)。它永遠不會被稱為“包含空”。

用法:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5



我喜歡Nat Pryce的文章。 以下是鏈接:

在文章中還有一個指向Java Maybe Type的Git存儲庫的鏈接,我發現它很有趣,但我不認為它可以減少檢查代碼的膨脹。在對互聯網進行一些研究之後,我認為!=空代碼膨脹主要是通過精心設計來減少的。




Java 8或更新版本的最佳替代方案可能是使用Optional該類。

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

對於可能為空的值的長鏈,這尤其方便。 例:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

有關如何在null上拋出異常的示例:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7引入了Objects.requireNonNull一種方法,當檢查某些內容是否為非null時,該方法很方便。例:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();



我高度忽略了建議在每種情況下使用null對象的答案。這種模式可能會破壞合同並將問題深入和深入地埋沒而不是解決它們,沒有提到使用不當會產生另一堆樣板代碼,這將需要將來的維護。

實際上,如果從方法返回的內容可以為null並且調用代碼必須對此做出決定,那麼應該有一個確保狀態的早期調用。

還要記住,如果毫無顧慮地使用空對像模式將是內存飢餓。為此 - 應該在所有者之間共享NullObject的實例,而不是每個實例的unigue實例。

此外,我不建議使用此模式,其中類型是基本類型表示 - 如數學實體,不是標量:向量,矩陣,複數和POD(普通舊數據)對象,它們旨在保持狀態以Java內置類型的形式。在後一種情況下,您最終會調用具有任意結果的getter方法。例如,NullPerson.getName()方法應返回什麼?

值得考慮這些案例以避免荒謬的結果。




這是大多數開發人員最常出現的錯誤。

我們有很多方法可以解決這個問題。

方法1:

org.apache.commons.lang.Validate //using apache framework

notNull(Object object,String message)

方法2:

if(someObject!=null){ // simply checking against null
}

方法3:

@isNull @Nullable  // using annotation based validation

方法4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}



Related