Java: How to check for null pointers efficiently

2019-01-11 03:32发布

There are some patterns for checking whether a parameter to a method has been given a null value.

First, the classic one. It is common in self-made code and obvious to understand.

public void method1(String arg) {
  if (arg == null) {
    throw new NullPointerException("arg");
  }
}

Second, you can use an existing framework. That code looks a little nicer because it only occupies a single line. The downside is that it potentially calls another method, which might make the code run a little slower, depending on the compiler.

public void method2(String arg) {
  Assert.notNull(arg, "arg");
}

Third, you can try to call a method without side effects on the object. This may look odd at first, but it has fewer tokens than the above versions.

public void method3(String arg) {
  arg.getClass();
}

I haven't seen the third pattern in wide use, and it feels almost as if I had invented it myself. I like it for its shortness, and because the compiler has a good chance of optimizing it away completely or converting it into a single machine instruction. I also compile my code with line number information, so if a NullPointerException is thrown, I can trace it back to the exact variable, since I have only one such check per line.

Which check do you prefer, and why?

14条回答
啃猪蹄的小仙女
2楼-- · 2019-01-11 03:40

The first option is the easiest one and also is the most clear.

It's not common in Java, but in C and C++ where the = operator can be included in a expression in the if statement and therefore lead to errors, it's often recommended to switch places between the variable and the constant like this:

if (NULL == variable) {
   ...
}

instead of:

if (variable == NULL) {
   ...
}

preventing errors of the type:

if (variable = NULL) { // Assignment!
   ...
}

If you make the change, the compiler will find that kind of errors for you.

查看更多
一夜七次
3楼-- · 2019-01-11 03:46

x==null is super fast, and it can be a couple of CPU clocks (incl. the branch prediction which is going to succeed). AssertNotNull will be inlined, so no difference there.

x.getClass() should not be faster than x==null even if it uses trap. (reason: the x will be in some register and checking a register vs an immediate value is fast, the branch is going to be predicted properly as well)

Bottom line: unless you do something truly weird, it'd be optimized by the JVM.

查看更多
祖国的老花朵
4楼-- · 2019-01-11 03:48

Approach #3: arg.getClass(); is clever, but unless this idiom see widespread adoption, I'd prefer the clearer, more verbose methods as opposed to saving a few characters. I'm a "write once, read many" kind of programmer.

The other approaches are self-documenting: there's a log message you can use to clarify what happened - this log message is use when reading the code and also at run-time. arg.getClass(), as it stands, is not self-documenting. You could use a comment at least o clarify to reviewers of the code:

arg.getClass(); // null check

But you still don't get a chance to put a specific message in the runtime like you can with the other methods.


Approach #1 vs #2 (null-check+NPE/IAE vs assert): I try to follow guidelines like this:

http://data.opengeo.org/GEOT-290810-1755-708.pdf

  • Use assert to check parameters on private methods
    assert param > 0;

  • Use null check + IllegalArgumentException to check parameters on public methods
    if (param == null) throw new IllegalArgumentException("param cannot be null");

  • Use null check + NullPointerException where needed
    if (getChild() == null) throw new NullPointerException("node must have children");


HOWEVER, since this is question may be about catching potential null issues most efficiently, then I have to mention my preferred method for dealing with null is using static analysis, e.g. type annotations (e.g. @NonNull) a la JSR-305. My favorite tool for checking them is:

The Checker Framework:
Custom pluggable types for Java
https://checkerframework.org/manual/#checker-guarantees

If its my project (e.g. not a library with a public API) and if I can use the Checker Framework throughout:

  • I can document my intention more clearly in the API (e.g. this parameter may not be null (the default), but this one may be null (@Nullable; the method may return null; etc). This annotation is right at the declaration, rather than further away in the Javadoc, so is much more likely to be maintained.

  • static analysis is more efficient than any runtime check

  • static analysis will flag potential logic flaws in advance (e.g. that I tried to pass a variable that may be null to a method that only accepts a non-null parameter) rather than depending on the issue occurring at runtime.

One other bonus is that the tool lets me put the annotations in a comment (e.g. `/@Nullable/), so my library code can compatible with type-annotated projects and non-type-annotated projects (not that I have any of these).


In case the link goes dead again, here's the section from GeoTools Developer Guide:

http://data.opengeo.org/GEOT-290810-1755-708.pdf

5.1.7 Use of Assertions, IllegalArgumentException and NPE

The Java language has for a couple of years now made an assert keyword available; this keyword can be used to perform debug only checks. While there are several uses of this facility, a common one is to check method parameters on private (not public) methods. Other uses are post-conditions and invariants.

Reference: Programming With Assertions

Pre-conditions (like argument checks in private methods) are typically easy targets for assertions. Post-conditions and invariants are sometime less straighforward but more valuable, since non-trivial conditions have more risks to be broken.

  • Example 1: After a map projection in the referencing module, an assertion performs the inverse map projection and checks the result with the original point (post-condition).
  • Example 2: In DirectPosition.equals(Object) implementations, if the result is true, then the assertion ensures that hashCode() are identical as required by the Object contract.

Use Assert to check Parameters on Private methods

private double scale( int scaleDenominator ){
 assert scaleDenominator > 0;
 return 1 / (double) scaleDenominator;
}

You can enable assertions with the following command line parameter:

java -ea MyApp

You can turn only GeoTools assertions with the following command line parameter:

java -ea:org.geotools MyApp

You can disable assertions for a specific package as shown here:

java -ea:org.geotools -da:org.geotools.referencing MyApp

Use IllegalArgumentExceptions to check Parameters on Public Methods

The use of asserts on public methods is strictly discouraged; because the mistake being reported has been made in client code - be honest and tell them up front with an IllegalArgumentException when they have screwed up.

public double toScale( int scaleDenominator ){
 if( scaleDenominator > 0 ){
 throw new IllegalArgumentException( "scaleDenominator must be greater than 0");
 }
 return 1 / (double) scaleDenominator;
}

Use NullPointerException where needed

If possible perform your own null checks; throwing a IllegalArgumentException or NullPointerException with detailed information about what has gone wrong.

public double toScale( Integer scaleDenominator ){
 if( scaleDenominator == null ){
 throw new NullPointerException( "scaleDenominator must be provided");
 }
 if( scaleDenominator > 0 ){
 throw new IllegalArgumentException( "scaleDenominator must be greater than 0");
 }
 return 1 / (double) scaleDenominator;
}
查看更多
Ridiculous、
5楼-- · 2019-01-11 03:57

You should not be throwing NullPointerException. If you want a NullPointerException, just dont check the value and it will be thrown automatically when the parameter is null and you attempt to dereference it.

Check out the apache commons lang Validate and StringUtils classes.
Validate.notNull(variable) it will throw an IllegalArgumentException if "variable" is null.
Validate.notEmpty(variable) will throw an IllegalArgumentException if "variable" is empty (null or zero length".
Perhaps even better:
String trimmedValue = StringUtils.trimToEmpty(variable) will guarantee that "trimmedValue" is never null. If "variable" is null, "trimmedValue" will be the empty string ("").

查看更多
劳资没心,怎么记你
6楼-- · 2019-01-11 03:57

The first method is my preference because it conveys the most intent. There are often shortcuts that can be taken in programming but my view is that shorter code is not always better code.

查看更多
The star\"
7楼-- · 2019-01-11 03:59

I'd use the built-in Java assert mechanism.

assert arg != null;

The advantage of this over all the other methods is that it can be switched off.

查看更多
登录 后发表回答