Code Advice #9: Avoid null - use objects

(See intro for a background and caveats on these coding advice blog entries.)

When a method is supposed to return an object, and it returns "null", the null is used to convey a number of meanings:

  • Empty (when it's supposed to return an array or a list, but the list is empty)
  • Can't (when some error happens and an object to be constructed failed; the caller is now supposed to gracefully handle the error)
  • Unknown (when a method is supposed to compute some result that it couldn't figure out)
...and probably many more.

Whenever possible, avoid using nulls. There are several better alternatives.

  1. When a method needs to return an array, and you know the array is empty, don't be lazy and just return null. Perhaps you're not being lazy, but believe you're being efficient, because by returning null you can communicate empty to the caller without having to construct an object (the empty array or list). However, the performance impact is minimal. It's very cheap to construct short-lived objects in Java - much less than malloc cost in C. However, you can use the following pattern if this is going to be frequent:
          class Window {
              private static final Window[] NO_WINDOWS = new Window[0];
     
              public getActiveWindows() {
                  if (whatever) {
                      return null;
                      return NO_WINDOWS;
                  }
                  ...
              }
              ...
        
    An approach like this has the advantage that you can start simplifying your own code, since you won't be doing null checks everywhere. If you call the getActiveWindows() method above, you can simply iterate over the results directly rather than branching on null first.

  2. When the "null" return value means something like "unknown", it's even better to use a special marker object. This has a number of advantages.
    • The intent is more clearly communicated, so the code is easier to read.

    • The marker object can customize some of its methods such that it reacts appropriately. This allows you to move some handling code into the object itself rather than in the client code. (As a simple example, your toString method can be written to be more informative.)

    • The class containing the marker object can also be written to consider marker objects in a correct way; for example, the right equals() semantics can be written.

    The above is a bit abstract so let me illustrate with another example. Let's say we're writing a word processor. We have a Document class, representing a document, and a Caret class, representing a visual caret (text insertion cursor). The caret has an associated position in the document, and this is represented by a Position object. Thus, the caret has the following method:
          public Position getPosition();
        
    Sometimes a document doesn't have an associated caret position - for example, when the document does not have focus. We could simply have getPosition() return null for this. But that would mean that client code relying on positions would always need to check for null before calling methods on the position they receive. Instead, the Position class should have a static field representing a non-existent position:
     
           class Position {
               public static final Position NONE = new Position(xxx);
               ...
        
    Now, most client code can simply call Position methods without worrying whether it will be null or not. The Position class itself can be modified such that various methods handle the NONE case correctly: not only can toString() do something like this:
            public String toString() {
                if (this == NONE) {
                    return "Position.NONE";
                } else {
                    ...
        
    but for example position comparison methods can have special considerations for how to handle positions where one or more represent the nonexistent position. Yes, this could be achieved by consistent use of null instead, but it's a lot less readable, and harder to search.

Null handling has gotten some renewed attention recently. With the advent of annotations, you can annotate whether a method is expected to return null or not (e.g. with @notnull). This allows tools to perform static analysis of your code and find potential bugs. For example, if you indicate that a method cannot return null, the tool can obviously flag any "return null" statements within that method (or returning expressions calling methods that may return null). But they can also flag code calling this method that unnecessarily check the return value for null, and more importantly, flag missing return value checks in code calling methods that may return null.

IntelliJ has added this feature already. Looks like it has been around for at least 10 years with lint checkers in C. I look forward to getting this in my favorite IDE (why oh why isn't @notnull and @nullable part of the standard annotations set?) - but in any case, try to avoid nulls by using place holder objects where appropriate.

Comments:

There is also a Design Pattern that deals with this subject: NullObject Pattern. Whereas the name NullObject is a bit misleading, the pattern deals exactly with what you describe: Don't return nulls; return objects instead.

Posted by Andreas Guther on February 26, 2006 at 02:58 PM PST #

Post a Comment:
Comments are closed for this entry.
About

Tor Norbye

Search

Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today