java - exception中文 pointer 避免!= null語句




15 Answers

如果您使用(或計劃使用)Java IDE,如JetBrains IntelliJ IDEA ,Eclipse或Netbeans或像findbugs這樣的工具,那麼您可以使用註釋來解決此問題。

基本上,你有@Nullable@NotNull

您可以在方法和參數中使用,如下所示:

@NotNull public static String helloWorld() {
    return "Hello World";
}

要么

@Nullable public static String helloWorld() {
    return "Hello World";
}

第二個示例將無法編譯(在IntelliJ IDEA中)。

在另一段代碼中使用第一個helloWorld()函數時:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

現在,IntelliJ IDEA編譯器會告訴您檢查沒用,因為helloWorld()函數永遠不會返回null

使用參數

void someMethod(@NotNull someParameter) { }

如果你寫的東西像:

someMethod(null);

這不會編譯。

最後一個使用@Nullable例子

@Nullable iWantToDestroyEverything() { return null; }

這樣做

iWantToDestroyEverything().something();

你可以肯定這不會發生。 :)

這是一種很好的方式讓編譯器檢查比通常更多的東西,並強制你的合同更強大。 不幸的是,並非所有編譯器都支持它。

在IntelliJ IDEA 10.5及@Nullable ,他們添加了對任何其他@Nullable @NotNull實現的支持。

請參閱博客文章更靈活和可配置的@Nullable / @NotNull註釋

nullpointerexception意思

我使用object != null來避免NullPointerException

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

例如:

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

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

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




哇,當我們有57種不同的推薦NullObject pattern方法時,我幾乎NullObject pattern添加另一個答案,但我認為有些人對這個問題感興趣可能想知道桌面上有一個提議,要求Java 7添加“null” -safe handling“ - if-not-equal-null邏輯的簡化語法。

Alex Miller給出的例子如下:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

?. 表示僅在左標識符不為空時才解除引用,否則將表達式的其餘部分計算為null 。 有些人,比如Java Posse成員Dick Wall和Devoxx選民都非常喜歡這個提議,但也存在反對意見,理由是它實際上會鼓勵更多地使用null作為哨兵價值。

更新:已在Project Coin下提交了Java 7中關於零安全運算符的proposed 語法與上面的示例略有不同,但它是相同的概念。

更新:零安全運營商提案未進入項目硬幣。 因此,您將不會在Java 7中看到此語法。




僅限於這種情況 -

在調用equals方法之前不檢查變量是否為null(下面的字符串比較示例):

if ( foo.equals("bar") ) {
 // ...
}

如果foo不存在,將導致NullPointerException

如果你像這樣比較你的String ,你可以避免這種情況:

if ( "bar".equals(foo) ) {
 // ...
}



根據您檢查的對像類型,您可以使用apache公共中的一些類,例如: apache commons langapache commons collections

例:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

或(取決於您需要檢查的內容):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

StringUtils類只是其中之一; 在公共場所中有很多優秀的類可以安全操作。

下面是一個如何在包含apache庫(commons-lang-2.4.jar)時在JAVA中使用null vallidation的示例

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

如果您使用的是Spring,Spring在其包中也具有相同的功能,請參閱library(spring-2.4.6.jar)

有關如何從spring使用此靜態classf的示例(org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");



我是“快速失敗”代碼的粉絲。 問問自己 - 在參數為null的情況下,你是否正在做一些有用的事情? 如果你沒有明確答案代碼在這種情況下應該做什麼......也就是說它首先應該永遠不為null,然後忽略它並允許拋出NullPointerException。 調用代碼與IllegalArgumentException一樣具有NPE意義,但是如果拋出NPE而不是代碼試圖執行其他意外的意外事件,開發人員將更容易調試並理解出現了什麼問題。邏輯 - 最終導致應用程序失敗。




有時,您有一些方法可以對其參數進行操作,從而定義對稱操作:

a.f(b); <-> b.f(a);

如果你知道b永遠不能為空,你可以交換它。 它對equals最有用:而不是foo.equals("bar"); 更好地做"bar".equals(foo);




Java 7有一個新的java.util.Objects實用程序類,其中有一個requireNonNull()方法。所有這一切都是拋出一個NullPointerException如果它的參數為null,但它會稍微清理一下代碼。例:

Objects.requireNonNull(someObject);
someObject.doCalc();

該方法對於在構造函數中的賦值之前進行checking非常有用,其中每次使用它都可以保存三行代碼:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}



最終,完全解決此問題的唯一方法是使用不同的編程語言:

  • 在Objective-C中,你可以相當於調用一個方法nil,絕對不會發生任何事情。這使得大多數空檢查變得不必要,但它可以使錯誤更難以診斷。
  • Nice是一種Java派生語言,所有類型都有兩個版本:潛在的null版本和非null的版本。您只能在非null類型上調用方法。通過顯式檢查null,可以將可能為空的類型轉換為非null類型。這使得更容易知道哪些空檢查是必要的,哪些不是。



問這個問題指出你可能對錯誤處理策略感興趣。您的團隊的架構師應該決定如何處理錯誤。做這件事有很多種方法:

  1. 允許異常通過 - 在'主循環'或其他一些管理例程中捕獲它們。

    • 檢查錯誤情況並適當處理它們

當然也要看看面向方面編程 - 它們有很好的方法可以插入if( o == null ) handleNull()到你的字節碼中。




只是不要使用null。不要允許它。

在我的類中,大多數字段和局部變量都具有非空的默認值,並且我在代碼中的任何地方添加了契約語句(always-on asserts)以確保執行它(因為它更簡潔,更具表現力而不是讓它作為NPE出現,然後必須解決行號等)。

一旦我採用了這種做法,我注意到問題似乎已經解決了。你會在開發過程中更早地發現事情,並意識到你有一個弱點......更重要的是......它有助於封裝不同模塊的問題,不同的模塊可以互相“信任”,不再亂扔垃圾代碼與if = null else構造!

這是防禦性編程,從長遠來看會產生更清晰的代碼。始終清理數據,例如通過強制執行嚴格的標準,問題就會消失。

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

合同就像迷你單元測試一樣,即使在生產中也一直在運行,當事情失敗時,你知道為什麼,而不是隨機的NPE,你必須以某種方式弄清楚。




這對每個Java開發人員來說都是一個非常普遍的問題 因此,Java 8中有官方支持來解決這些問題而不會出現混亂的代碼。

Java 8已經介紹過了java.util.Optional<T>。它是一個容器,可能包含也可能不包含非null值。Java 8提供了一種更安全的方法來處理在某些情況下其值可能為null的對象。它的靈感來自HaskellScala的想法。

簡而言之,Optional類包括顯式處理值存在或不存在的情況的方法。但是,與null引用相比的優點是Optional <T>類強制您在值不存在時考慮這種情況。因此,您可以防止意外的空指針異常。

在上面的示例中,我們有一個家庭服務工廠,它返回家中可用的多個設備的句柄。但這些服務可能有效,也可能無效; 這意味著它可能導致NullPointerException。不要if在使用任何服務之前添加空條件,而是將其包裝到Optional <Service>中。

包裝選項<T>

讓我們考慮一種從工廠獲取服務引用的方法。而不是返回服務引用,請使用Optional包裝它。它允許API用戶知道返回的服務可能或可能不可用/可用,防禦性地使用

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

如您所見,Optional.ofNullable()提供了一種簡單的方法來獲取引用。有另一種方式來獲得的可選,無論是參考Optional.empty()Optional.of()。一個用於返回空對象而不是重新調整null,另一個用於包裝不可為空的對象。

那麼它如何幫助避免空檢查?

一旦包裝了引用對象,Optional就會提供許多有用的方法來在沒有NPE的情況下調用包裝引用上的方法。

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent如果它是非空值,則使用引用調用給定的Consumer。否則,它什麼都不做。

@FunctionalInterface
public interface Consumer<T>

表示接受單個輸入參數且不返回結果的操作。與大多數其他功能接口不同,消費者需要通過副作用進行操作。它非常乾淨,易於理解。在上面的代碼示例中,HomeService.switchOn(Service)如果Optional保持引用為非null ,則調用gets。

我們經常使用三元運算符來檢查空條件並返回替代值或默認值。Optional提供了另一種處理相同條件的方法,而不檢查null。如果Optional具有空值,則Optional.orElse(defaultObj)返回defaultObj。我們在示例代碼中使用它:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

現在HomeServices.get()做了同樣的事情,但是以更好的方式。它檢查服務是否已初始化。如果是,則返回相同或創建新的新服務。可選<T> .orElse(T)有助於返回默認值。

最後,這是我們的NPE以及無空檢查代碼:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

完整的帖子是NPE以及Null免檢代碼......真的嗎?




我試過了,NullObjectPattern但對我來說並不總是最好的方式。有時候“不採取行動”是不恰當的。

NullPointerException是一個運行時異常,這意味著它是開發人員的錯誤,並且具有足夠的經驗,它會告訴您錯誤的確切位置。

現在回答:

盡量使所有屬性及其訪問者盡可能保密,或者避免將它們暴露給客戶端。當然,您可以在構造函數中包含參數值,但是通過減小範圍,您不會讓客戶端類傳遞無效值。如果需要修改值,可以隨時創建新值object。您只檢查構造函數中的值一次,並且在其餘方法中幾乎可以確定值不為null。

當然,經驗是理解和應用此建議的更好方式。

字節!




我可以更一般地回答它!

當方法以我們不期望的方式獲取參數時,我們通常會遇到這個問題(錯誤的方法調用是程序員的錯誤)。例如:您希望獲得一個對象,而不是一個null。你希望得到一個至少有一個字符的字符串,而不是你得到一個空字符串......

所以兩者之間沒有區別:

if(object == null){
   //you called my method badly!

}

要么

if(str.length() == 0){
   //you called my method badly again!
}

在我們執行任何其他功能之前,他們都希望確保我們收到有效參數。

正如其他一些答案中所提到的,為了避免上述問題,您可以按照合同模式設計。請參閱http://en.wikipedia.org/wiki/Design_by_contract

要在java中實現此模式,您可以使用核心java註釋(如javax.annotation.NotNull)或使用更複雜的庫(如Hibernate Validator)

只是一個樣本:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

現在,您可以安全地開發方法的核心功能,而無需檢查輸入參數,它們可以保護您的方法免受意外參數的影響。

您可以更進一步,確保只能在您的應用程序中創建有效的pojos。(來自hibernate驗證器站點的示例)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}



  1. 永遠不要將變量初始化為null。
  2. 如果(1)不可能,則將所有集合和數組初始化為空集合/數組。

在您自己的代碼中執行此操作,您可以避免!= null檢查。

大多數情況下,null檢查似乎保護集合或數組的循環,因此只需將它們初始化為空,就不需要任何空檢查。

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

這有一個很小的開銷,但是對於更乾淨的代碼和更少的NullPointerExceptions來說它是值得的。




public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}



Related