Skip to main content

skip to main content

developerWorks  >  Java technology  >

Diagnosing Java Code: The Null Flag bug pattern

Avoid using null pointers as flags for exceptions

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Introductory

Eric Allen, Software Engineer, Cycorp, Inc.

01 Mar 2001

Often, our efforts to develop robust programs by substituting null pointers for exceptional conditions actually limits control flow to the ordinary paths of method invocation and return, and buries evidence of where an exceptional condition occured. In this column, Eric Allen shows how this bug pattern, which he calls the Null Flag bug pattern, produces unexpected results that are difficult to debug. As with the other bug patterns that we have discussed, you can minimize occurrences of this pattern by applying certain programming techniques.

The Null Flag bug pattern

In my last article, I showed how the substitution of null pointers for various base types of data is one of the most common causes of NullPointerExceptions. This time, I will show how substituting null pointers for exceptional conditions also tends to cause problems. Exceptional conditions in Java programs are ordinarily dealt with by throwing exceptions and catching them at an appropriate point of control. But you will often see methods that indicate such a condition by returning a null-pointer value (and, perhaps, printing a message to System.err). If the calling method does not explicitly check for a null pointer, it may attempt to dereference the return value and trigger a null-pointer exception.

As you might have guessed, I call this pattern the Null Flag bug pattern because it is caused by inconsistently using null pointers as flags for exceptional conditions.



Back to top


The cause

Let's consider the following simple bridge class from BufferedReaders to Iterators:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Iterator;


public class BufferedReaderIterator implements Iterator {

  private BufferedReader internal;

  public BufferedReaderIterator(BufferedReader _internal) {
    this.internal = _internal;
  }

  public boolean hasNext() {
    try {
    
      boolean result = true;

      // Let's suppose that lines in the underlying input stream are known
      // to be no greater than 80 characters long.
      internal.mark(80);

      if (this.next() == null) {
        result = false;
      }
      internal.reset();
      return result;
    }
    catch (IOException e) {
      System.err.println(e.toString());
      return false;
    }
  }

  public Object next() {
    try {
      return internal.readLine();
    }
    catch (IOException e) {
      System.err.println(e.toString());
      return null;
    }
  }

  public void remove() {

    // This iterator does not support the remove operation.
    throw new UnsupportedOperationException();
  }

}

Because this class serves as a bridge implementation of the Iterator interface, the code must catch IOExceptions from BufferedReader. Each of the methods handles IOExceptions by returning some default value. In the case of hasNext, the value false is returned. This is reasonable because, if an IOException is thrown, the client should not expect that another element can be retrieved from the Iterator. On the other hand, next simply returns null, both in the case of an IOException and, because it relies on the return value of internal.readLine(), in the case when internal is empty. But this is not what the client of an Iterator object would expect. Normally, when next is called on an Iterator with no more elements, it throws a NoSuchElementException. If a client of our Iterator is relying on this behavior, it may very likely attempt to dereference a null pointer returned from a call to next, resulting in a NullPointerException.

Whenever a NullPointerException occurs, check for scenarios like the one described above. Occurrences of this bug pattern are very common.



Back to top


Prevention

Despite their common occurrence, the use of null flags is very often unwarranted (as in the case above). Let's rewrite next so that it throws NoSuchElementException as expected:

  public Object next() {
    try {
      String result = internal.readLine();
      if (result == null) {
	throw new NoSuchElementException();
      }
      else {
	return result;
      }
    }
    catch (IOException e) {
    
      // The original exception is included in the message to notify the 
      // client that an IOException has occurred.
      throw new NoSuchElementException(e.toString());
    }
  }


Note that, to make the rest of the code work with this altered method, we would also have to:

  1. Import java.util.NoSuchElementException.
  2. Fix hasNext so that it no longer calls next to test. The simplest way to do this would be to simply call internal.readLine() directly.

An alternative to our handling of IOExceptions would be to catch them and throw RuntimeExceptions in their stead. Base your decision to do this on an assessment of how frequently IOExceptions are expected to occur on the target platform. If they will be frequent, then you might want to try recovering from them.

Any code that calls this new next method may have to deal with thrown NoSuchElementExceptions. Of course, the code may simply choose to ignore them and allow the program to abort. If so, the resultant error message and the place where the exception was thrown would be much more informative than a NullPointerException thrown by the original code. If the exception thrown were a checked exception (such as IOException) it would be even more helpful, because no client code for the class would compile unless it handled the exception. That way, we could eliminate even the possibility of certain errors occurring before the program is ever run. But, in this example, we couldn't throw such a checked exception without violating the Iterator interface. So, we've sacrificed some static checking for the sake of reusing code that operates on Iterators. Tensions such as these, between the goals of static checking and reuse, are quite common.



Back to top


Wrapup

Before I leave this topic, I should address the concerns of many programmers who use null flags regularly. Many programmers argue that it makes their programs more "robust." After all, they might say, a robust system handles varied situations gracefully rather than throwing exceptions at every little problem that comes up. But this argument overlooks the fact that exceptions are a great tool to increase the robustness of code, by allowing control to pass quickly during an exceptional condition to the place most appropriate for controlling it. The use of null flags, on the other hand, limits control flow to the ordinary paths of method invocation and return (until the whole program crashes, of course). Additionally, by using null flags in this way, the programmer effectively buries the evidence of where the exceptional condition occurred. Who knows how far that null pointer was passed from method to method before it was ever dereferenced? This simply makes it harder to diagnose errors and determine how to fix them. Experience has shown that code breaks often. Our primary concern should be to avoid such obfuscation and make diagnosis as easy as possible. Therefore, as a matter of principle, I strive to write code that signals exceptional conditions as soon as possible and attempts to recover only from exceptional conditions that do not indicate bugs in the program.

Even if you try to avoid using null flags in your own code, you will inevitably have to deal with legacy code that uses them. In fact, many of the Java library classes themselves, such as the Hashtable class and the BufferedReader class that we used above, use null flags. When using such classes, you can avoid bugs by explicitly checking whether an operation will return null before performing it. For example, with Hashtables, I always test with containsKey before calling get. But, even with such preventative measures, this bug pattern is one of the most common patterns encountered.

Here's the breakdown of this week's bug pattern:

  • Pattern: Null Flag
  • Symptoms: A code block that uses null pointers as flags for exceptional conditions is signalling a NullPointerException.
  • Cause: The calling methods are not checking for null pointers as return values.
  • Cures and preventions: Throw exceptions to signal exceptional conditions.

In the next article, I will discuss bug patterns related to class cast exceptions.



Resources

  • Be sure to read Eric Allen's previous Diagnosing Java Code columns on bug patterns:
  • One way to prevent inconsistent handling of exceptional conditions is aspect-oriented programming: a style of programming that modularizes the parts of a program that ordinarily cut across module boundaries. Check out AspectJ, an aspect-oriented extension of the Java language, with an implementation that supports many popular Java IDEs.

  • An approach to statically determining possible null-pointer exceptions is a technique known as set-based analysis. The Carnegie Mellon School of Computer Science Web site provides a short introduction to this approach, along with links to several technical publications on the topic.

  • The Division of Software Engineering at DePaul University has done some work on automated theorem provers to detect null-pointer exceptions in Java code.

  • Visit the Patterns Home Page for a good introduction to design patterns and how they are used.

  • Check out JUnit and catch more errors by making your code "test-infested."


About the author

Eric Allen has an A.B. in computer science and mathematics from Cornell University. He is currently the lead Java software developer at Cycorp, Inc., and a part-time graduate student in the programming languages team at Rice University. His research concerns the development of formal semantic models and extensions of the Java language, both at the source and bytecode levels. Currently, he is implementing a source-to-bytecode compiler for the NextGen programming language, an extension of the Java language with generic run-time types.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top