How to avoid null checking in Java?

后端 未结 30 3273
失恋的感觉
失恋的感觉 2020-11-21 04:43

I use object != null a lot to avoid NullPointerException.

Is there a good alternative to this?

For example I often use:



        
相关标签:
30条回答
  • 2020-11-21 05:16

    With Java 8 comes the new java.util.Optional class that arguably solves some of the problem. One can at least say that it improves the readability of the code, and in the case of public APIs make the API's contract clearer to the client developer.

    They work like that:

    An optional object for a given type (Fruit) is created as the return type of a method. It can be empty or contain a Fruit object:

    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();
    }
    

    Now look at this code where we search a list of Fruit (fruits) for a given Fruit instance:

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

    You can use the map() operator to perform a computation on--or extract a value from--an optional object. orElse() lets you provide a fallback for missing values.

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

    Of course, the check for null/empty value is still necessary, but at least the developer is conscious that the value might be empty and the risk of forgetting to check is limited.

    In an API built from scratch using Optional whenever a return value might be empty, and returning a plain object only when it cannot be null (convention), the client code might abandon null checks on simple object return values...

    Of course Optional could also be used as a method argument, perhaps a better way to indicate optional arguments than 5 or 10 overloading methods in some cases.

    Optional offers other convenient methods, such as orElse that allow the use of a default value, and ifPresent that works with lambda expressions.

    I invite you to read this article (my main source for writing this answer) in which the NullPointerException (and in general null pointer) problematic as well as the (partial) solution brought by Optional are well explained: Java Optional Objects.

    0 讨论(0)
  • 2020-11-21 05:16
    1. Never initialise variables to null.
    2. If (1) is not possible, initialise all collections and arrays to empty collections/arrays.

    Doing this in your own code and you can avoid != null checks.

    Most of the time null checks seem to guard loops over collections or arrays, so just initialise them empty, you won't need any null checks.

    // 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
        }
    }
    

    There is a tiny overhead in this, but it's worth it for cleaner code and less NullPointerExceptions.

    0 讨论(0)
  • 2020-11-21 05:17

    If null-values are not allowed

    If your method is called externally, start with something like this:

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

    Then, in the rest of that method, you'll know that object is not null.

    If it is an internal method (not part of an API), just document that it cannot be null, and that's it.

    Example:

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

    However, if your method just passes the value on, and the next method passes it on etc. it could get problematic. In that case you may want to check the argument as above.

    If null is allowed

    This really depends. If find that I often do something like this:

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

    So I branch, and do two completely different things. There is no ugly code snippet, because I really need to do two different things depending on the data. For example, should I work on the input, or should I calculate a good default value?


    It's actually rare for me to use the idiom "if (object != null && ...".

    It may be easier to give you examples, if you show examples of where you typically use the idiom.

    0 讨论(0)
  • 2020-11-21 05:17

    The Google collections framework offers a good and elegant way to achieve the null check.

    There is a method in a library class like this:

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

    And the usage is (with import static):

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

    Or in your example:

    checkNotNull(someobject).doCalc();
    
    0 讨论(0)
  • 2020-11-21 05:18

    Sometimes, you have methods that operate on its parameters that define a symmetric operation:

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

    If you know b can never be null, you can just swap it. It is most useful for equals: Instead of foo.equals("bar"); better do "bar".equals(foo);.

    0 讨论(0)
  • 2020-11-21 05:18

    I highly disregard answers that suggest using the null objects in every situation. This pattern may break the contract and bury problems deeper and deeper instead of solving them, not mentioning that used inappropriately will create another pile of boilerplate code that will require future maintenance.

    In reality if something returned from a method can be null and the calling code has to make decision upon that, there should an earlier call that ensures the state.

    Also keep in mind, that null object pattern will be memory hungry if used without care. For this - the instance of a NullObject should be shared between owners, and not be an unigue instance for each of these.

    Also I would not recommend using this pattern where the type is meant to be a primitive type representation - like mathematical entities, that are not scalars: vectors, matrices, complex numbers and POD(Plain Old Data) objects, which are meant to hold state in form of Java built-in types. In the latter case you would end up calling getter methods with arbitrary results. For example what should a NullPerson.getName() method return?

    It's worth considering such cases in order to avoid absurd results.

    0 讨论(0)
提交回复
热议问题