Java: How to check for null pointers efficiently

前端 未结 14 1718
清歌不尽
清歌不尽 2020-12-03 01:17

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

相关标签:
14条回答
  • 2020-12-03 01:52

    While I agree with the general consensus of preferring to avoid the getClass() hack, it is worth noting that, as of OpenJDK version 1.8.0_121, javac will use the getClass() hack to insert null checks prior to creating lambda expressions. For example, consider:

    public class NullCheck {
      public static void main(String[] args) {
        Object o = null;
        Runnable r = o::hashCode;
      }
    }
    

    After compiling this with javac, you can use javap to see the bytecode by running javap -c NullCheck. The output is (in part):

    Compiled from "NullCheck.java"
    public class NullCheck {
      public NullCheck();
        Code:
           0: aload_0
           1: invokespecial #1    // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: aconst_null
           1: astore_1
           2: aload_1
           3: dup
           4: invokevirtual #2    // Method java/lang/Object.getClass:()Ljava/lang/Class;
           7: pop
           8: invokedynamic #3, 0 // InvokeDynamic #0:run:(Ljava/lang/Object;)Ljava/lang/Runnable;
          13: astore_2
          14: return
    }
    

    The instruction set at "lines" 3, 4 and 7 are basically invoking o.getClass(), and discarding the result. If you run NullCheck, you'll get a NullPointerException thrown from line 4.

    Whether this is something that the Java folks concluded was a necessary optimization, or it is just a cheap hack, I don't know. However, based on John Rose's comment at https://bugs.openjdk.java.net/browse/JDK-8042127?focusedCommentId=13612451&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13612451, I suspect that it may indeed be the case that the getClass() hack, which produces an implicit null check, may be ever so slightly more performant than its explicit counterpart. That said, I would avoid using it unless careful benchmarking showed that it made any appreciable difference.

    (Interestingly, the Eclipse Compiler For Java (ECJ) does not include this null check, and running NullCheck as compiled by ECJ will not throw a n NPE.)

    0 讨论(0)
  • 2020-12-03 01:54

    First method. I would never do the second or the third method, not unless they are implemented efficiently by the underlying JVM. Otherwise, those two are just prime examples of premature optimization (with the third having a possible performance penalty - you don't want to be dealing and accessing class meta-data in general access points.)

    The problem with NPEs is that they are things that cross-cut many aspects of programming (and my aspects, I mean something deeper and more profound that AOP). It is a language design problem (not saying that the language is bad, but that it is one fundamental short-coming... of any language that allows null pointers or references.)

    As such, it is best to simply deal with it explicitly as in the first method. All other methods are (failed) attempts to simplify a model of operations, an unavoidable complexity that exists on the underlying programming model.

    It is a bullet that we cannot avoid to bite. Deal with it explicitly as it is - in the general case that is - the less painful down the road.

    0 讨论(0)
  • 2020-12-03 01:55

    In my opinion, there are three issues with the third method:

    1. The intent is unclear to the casual reader.
    2. Even though you have line number information, line numbers change. In a real production system, knowing that there was a problem in SomeClass at line 100 doesn't give you all the info you need. You also need to know the revision of the file in question and be able to get to that revision. All in all, a lot of hassle for what appears to be very little benefit.
    3. It is not at all clear why you think the call to arg.getClass can be optimized away. It is a native method. Unless HotSpot is coded to have specific knowledge of the method for this exact eventuality, it'll probably leave the call alone since it can't know about any potential side-effects of the C code that gets called.

    My preference is to use #1 whenever I feel there's a need for a null check. Having the variable name in the error message is great for quickly figuring out what exactly has gone wrong.

    P.S. I don't think that optimizing the number of tokens in the source file is a very useful criterion.

    0 讨论(0)
  • 2020-12-03 01:55

    I believe that the fourth and the most useful pattern is to do nothing. Your code will throw NullPointerException or other exception a couple of lines later (if null is illegal value) and will work fine if null is OK in this context.

    I believe that you should perform null check only if you have something to do with it. Checking to throw exception is irrelevant in most cases. Just do not forget to mention in javadoc whether the parameter can be null.

    0 讨论(0)
  • 2020-12-03 01:59

    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.

    0 讨论(0)
  • 2020-12-03 01:59

    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.

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