It doesn't take much Java development experience to learn firsthand what the NullPointerException is about. In fact, one person has highlighted dealing with this as the
number one mistake Java developers make. I
blogged previously on use of
String.value(Object) to reduce unwanted
NullPointerExceptions. There are
several other simple
techniques one can use to reduce or eliminate the
occurrences of this common type of
RuntimeException that has been with us since
JDK 1.0. This blog post collects and summarizes some of the most popular of these techniques.
Check Each Object For Null Before UsingThe most sure way to avoid a
NullPointerException is to check all object references to ensure that they are not null before accessing one of the object's fields or methods. As the following example indicates, this is a very simple technique.
final String causeStr = "adding String to Deque that is set to null.";
final String elementStr = "Fudd";
Deque<String> deque = null;
try
{
deque.push(elementStr);
log("Successful at " + causeStr, System.out);
}
catch (NullPointerException nullPointer)
{
log(causeStr, nullPointer, System.out);
}
try
{
if (deque == null)
{
deque = new LinkedList<String>();
}
deque.push(elementStr);
log( "Successful at " + causeStr
+ " (by checking first for null and instantiating Deque implementation)",
System.out);
}
catch (NullPointerException nullPointer)
{
log(causeStr, nullPointer, System.out);
}
In the code above, the
Deque used is intentionally initialized to null to facilitate the example. The code in the first
try
block does not check for null before trying to access a
Deque method. The code in the second
try
block does check for null and instantiates an implementation of the
Deque
(
LinkedList) if it is null. The output from both examples looks like this:
ERROR: NullPointerException encountered while trying to adding
String to Deque that is set to null.
java.lang.NullPointerException
INFO: Successful at adding String to Deque that is set to null.
(by checking first for null and instantiating Deque implementation)
The message following ERROR in the output above indicates that a
NullPointerException
is thrown when a method call is attempted on the null
Deque
. The message following INFO in the output above indicates that by checking
Deque
for null first and then instantiating a new implementation for it when it is null, the exception was avoided altogether.
This approach is often used and, as shown above, can be very useful in avoiding unwanted (unexpected)
NullPointerException
instances. However, it is not without its costs. Checking for null before using every object can bloat the code, can be tedious to write, and opens more room for problems with development and maintenance of the additional code. For this reason, there has been talk of introducing Java language support for
built-in null detection,
automatic adding of these checks for null after the initial coding,
null-safe types, use of
Aspect-Oriented Programming (
AOP) to add
null checking to byte code, and other
null-detection tools.
Groovy already provides a
convenient mechanism for dealing with object references that are potentially null. Groovy's
safe navigation operator (
?.
) returns null rather than throwing a
NullPointerException
when a null object reference is accessed.
Because checking null for every object reference can be tedious and does bloat the code, many developers choose to judiciously select which objects to check for null. This typically leads to checking of null on all objects of
potentially unknown origins. The idea here is that objects can be checked at exposed interfaces and then be assumed to be safe after the initial check.
This is a situation where the
ternary operator can be particularly useful. Instead of
// retrieved a BigDecimal called someObject
String returnString;
if (someObject != null)
{
returnString = someObject.toEngineeringString();
}
else
{
returnString = "";
}
the ternary operator supports this more concise syntax
// retrieved a BigDecimal called someObject
final String returnString = (someObject != null)
? someObject.toEngineeringString()
: "";
}
Check Method Arguments for NullThe technique just discussed can be used on all objects. As stated in that technique's description, many developers choose to only check objects for null when they come from "untrusted" sources. This often means testing for null first thing in methods exposed to external callers. For example, in a particular class, the developer might choose to check for null on all objects passed to
public
methods, but not check for null in
private
methods.
The following code demonstrates this checking for null on method entry. It includes a single method as the demonstrative method that turns around and calls two methods, passing each method a single null argument. One of the methods receiving a null argument checks that argument for null first, but the other just assumes the passed-in parameter is not null.
/**
* Append predefined text String to the provided StringBuilder.
*
* @param builder The StringBuilder that will have text appended to it; should
* be non-null.
* @throws IllegalArgumentException Thrown if the provided StringBuilder is
* null.
*/
private void appendPredefinedTextToProvidedBuilderCheckForNull(
final StringBuilder builder)
{
if (builder == null)
{
throw new IllegalArgumentException(
"The provided StringBuilder was null; non-null value must be provided.");
}
builder.append("Thanks for supplying a StringBuilder.");
}
/**
* Append predefined text String to the provided StringBuilder.
*
* @param builder The StringBuilder that will have text appended to it; should
* be non-null.
*/
private void appendPredefinedTextToProvidedBuilderNoCheckForNull(
final StringBuilder builder)
{
builder.append("Thanks for supplying a StringBuilder.");
}
/**
* Demonstrate effect of checking parameters for null before trying to use
* passed-in parameters that are potentially null.
*/
public void demonstrateCheckingArgumentsForNull()
{
final String causeStr = "provide null to method as argument.";
logHeader("DEMONSTRATING CHECKING METHOD PARAMETERS FOR NULL", System.out);
try
{
appendPredefinedTextToProvidedBuilderNoCheckForNull(null);
}
catch (NullPointerException nullPointer)
{
log(causeStr, nullPointer, System.out);
}
try
{
appendPredefinedTextToProvidedBuilderCheckForNull(null);
}
catch (IllegalArgumentException illegalArgument)
{
log(causeStr, illegalArgument, System.out);
}
}
When the above code is executed, the output appears as shown next.
ERROR: NullPointerException encountered while trying to provide null to
method as argument.
java.lang.NullPointerException
ERROR: IllegalArgumentException encountered while trying to provide null
to method as argument.
java.lang.IllegalArgumentException: The provided StringBuilder was null;
non-null value must be provided.
In both cases, an error message was logged. However, the case in which a null was checked for threw an advertised
IllegalArgumentException that included additional context information about when the null was encountered. Alternatively, this null parameter could have been handled in a variety of ways. For the case in which a null parameter was not handled, there were no options for how to handle it. Many people prefer to throw a
NullPolinterException
with the additional context information when a null is explicitly discovered (see Item #60 in the Second Edition of
Effective Java or Item #42 in First Edition), but I have a slight preference for
IllegalArgumentException
when it is explicitly a method argument that is null because I think the very exception adds context details and it is easy to include "null" in the subject.
The technique of checking method arguments for null is really a subset of the more general technique of checking all objects for null. However, as outlined above, arguments to publicly exposed methods are often the least trusted in an application and so checking them may be more important than checking the average object for null.
Checking method parameters for null is also a subset of the more general practice of checking method parameters for general validity as discussed in Item #38 of the Second Edition of
Effective Java (Item 23 in First Edition).
Consider Primitives Rather than ObjectsI don't think it is a good idea to select a primitive data type (such as
int
) over its corresponding object reference type (such as
Integer) simply to avoid the possibility of a
NullPointerException
, but there is no denying that one of the advantages of primitive types is that they do not lead to
NullPointerException
s. However, primitives still must be checked for validity (a month cannot be a negative integer) and so this benefit may be small. On the other hand, primitives cannot be used in
Java Collections and there are times one wants the ability to set a value to null.
The most important thing is to be very cautious about the combination of primitives, reference types, and autoboxing. There is a warning in
Effective Java (Second Edition, Item #49) regarding the dangers, including throwing of
NullPointerException
, related to careless mixing of primitive and reference types.
Carefully Consider Chained Method CallsA
NullPointerException
can be very easy to find because a line number will state where it occurred. For example, a stack trace might look like that shown next:
java.lang.NullPointerException
at dustin.examples.AvoidingNullPointerExamples.
demonstrateNullPointerExceptionStackTrace(AvoidingNullPointerExamples.java:222)
at dustin.examples.
AvoidingNullPointerExamples.main(AvoidingNullPointerExamples.java:247)
The stack trace makes it obvious that the
NullPointerException
was thrown as a result of code executed on line 222 of
AvoidingNullPointerExamples.java
. Even with the line number provided, it can still be difficult to narrow down which object is null if there are multiple objects with methods or fields accessed on the same line.
For example, a statement like
someObject.getObjectA().getObjectB().getObjectC().toString();
has four possible calls that might have thrown the
NullPointerException
attributed to the same line of code. Using a debugger can help with this, but there may be situations when it is preferable to simply break the above code up so that each call is performed on a separate line. This allows the line number contained in a stack trace to easily indicate which exact call was the problem. Furthermore, it facilitates explicit checking each object for null. However, on the downside, breaking up the code increases the line of code count (
to some that's a positive!) and may not always be desirable, especially if one is certain none of the methods in question will ever be null.
Make NullPointerExceptions More InformativeIn the above recommendation, the warning was to consider carefully use of method call chaining primarily because it made having the line number in the stack trace for a
NullPointerException
less helpful than it otherwise might be. However, the line number is only shown in a stack trace when the code was compiled with the debug flag turned on. If it was compiled without debug, the stack trace looks like that shown next:
java.lang.NullPointerException
at dustin.examples.AvoidingNullPointerExamples.
demonstrateNullPointerExceptionStackTrace(Unknown Source)
at dustin.examples.AvoidingNullPointerExamples.main(Unknown Source)
As the above output demonstrates, there is a method name, but not no line number for the
NullPointerException
. This makes it more difficult to immediately identify what in the code led to the exception. One way to address this is to provide context information in any thrown
NullPointerException
. This idea was demonstrated earlier when a
NullPointerException
was caught and re-thrown with additional context information as a
IllegalArgumentException
. However, even if the exception is simply re-thrown as another
NullPointerException
with context information, it is still helpful. The context information helps the person debugging the code to more quickly identify the true cause of the problem.
The following example demonstrates this principle.
final Calendar nullCalendar = null;
try
{
final Date date = nullCalendar.getTime();
}
catch (NullPointerException nullPointer)
{
log("NullPointerException with useful data", nullPointer, System.out);
}
try
{
if (nullCalendar == null)
{
throw new NullPointerException("Could not extract Date from provided Calendar");
}
final Date date = nullCalendar.getTime();
}
catch (NullPointerException nullPointer)
{
log("NullPointerException with useful data", nullPointer, System.out);
}
The output from running the above code looks as follows.
ERROR: NullPointerException encountered while trying to NullPointerException
with useful data
java.lang.NullPointerException
ERROR: NullPointerException encountered while trying to
NullPointerException with useful data
java.lang.NullPointerException: Could not extract Date from provided Calendar
The first error does not provide any context information and only conveys that it is a
NullPointerException
. The second error, however, had explicit context information added to it which would go a long way in helping identify the source of the exception.
Use String.valueOf Rather than toStringAs described previously, one of the surest methods for avoiding
NullPointerException
is to check the object being referenced for null first. The
String.valueOf(Object) method is a good example of a case where this check for null can be done implicitly without any additional effort on the developer's part. I
blogged on this previously, but include a brief example of its use here.
Source : Inspired by actual events