Better NPE Messages in JDK 14
My March 2019 blog post “Better Default NullPointerException Messages Coming to Java?” was written when the draft JEP for better NullPointerException messages had not yet been targeted to a particular JDK release. Since then, that draft JEP became JEP 358 (“Helpful NullPointerExceptions”), which has been targeted for JDK 14. Even better, the initial implementation (JDK-8218628) is already in the JDK 14 branch and is available to play with in JDK 14 Early Access Builds Build 20 (2019/10/23).
In this post, I will run the example code introduced in my previous post against the JDK 14 Early Access Build 20 to demonstrate the additional details now provided. To see that example code that was written to intentionally introduce a variety of situations resulting in NullPointerException
s, see that earlier post or view the source code on GitHub.
With the JDK 14 Early Access Build 20 downloaded and pointed to by my path, I see the following when I run java -version
:
1 2 3 | openjdk version "14-ea" 2020 - 03 - 17 OpenJDK Runtime Environment (build 14 -ea+ 20 - 879 ) OpenJDK 64 -Bit Server VM (build 14 -ea+ 20 - 879 , mixed mode, sharing) |
With the JDK 14 Early Access Build 20 configured appropriately, I rebuilt the source code mentioned previously and then re-ran the code with the java launcher without any new options. The output from this (shown below) is not materially different from the output with previous JDK versions.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | ========================================= | # 1 : Element [ 0 ] on null boolean array | ========================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateFirstExampleIndexAccessOnNullBooleanArray(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ================================= | # 2 : .length on null boolean [] | ================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateSecondExampleLengthOnNullBooleanArray(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ======================================= | # 3 : Assigning float to null float [] | ======================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateThirdExampleAssigningValueToElementOfNullFloatArray(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ====================================== | # 4 : Accessing field on null object | ====================================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateFourthExampleAccessInstanceFieldOfNullObject(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) =================== | # 5 : throw null ; | =================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateFifthExampleThrowingConstantNull(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ================================================ | # 6 : Method invocation on null instance field | ================================================ java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateSixthExampleMethodInvocationOnNullInstanceField(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ============================================= | # 7 : synchronized () on null instance field | ============================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateSeventhExampleSynchronizedNullInstanceField(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ========================================================================== | >>> Null Lost in Long Series of Method Invocations in Single Statement | ========================================================================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateNullLostInSeriesOfMethodInvocationsInSingleStatement(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ======================================================= | >>> Null Lost in Dereferenced Constructor Arguments | ======================================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateNullLostInConstructorAcceptingMultiplePotentiallyNullArgumentsDereferenced(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ================================================== | >>> Null Lost in Dereferenced Method Arguments | ================================================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateNullLostInMethodAcceptingMultiplePotentiallyNullArgumentsDereferenced(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) |
As the above output demonstrates, even with the new JDK 14 Early Access Build 20, I don’t see any new detailed information regarding NullPointerException
s when I run my application as normal. I included this output to show that a special flag is needed to enable the more detailed NullPointerException
s and to make it more convenient to compare the output without and with the extra details. The next output listing shows the additional details provided when the java launcher is passed the flag -XX:+ShowCodeDetailsInExceptionMessages
:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | ========================================= | # 1 : Element [ 0 ] on null boolean array | ========================================= java.lang.NullPointerException: Cannot load from byte / boolean array because "<local1>" is null at dustin.examples.npe.NpeDemo.demonstrateFirstExampleIndexAccessOnNullBooleanArray(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ================================= | # 2 : .length on null boolean [] | ================================= java.lang.NullPointerException: Cannot read the array length because "<local1>" is null at dustin.examples.npe.NpeDemo.demonstrateSecondExampleLengthOnNullBooleanArray(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ======================================= | # 3 : Assigning float to null float [] | ======================================= java.lang.NullPointerException: Cannot store to float array because "<local1>" is null at dustin.examples.npe.NpeDemo.demonstrateThirdExampleAssigningValueToElementOfNullFloatArray(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ====================================== | # 4 : Accessing field on null object | ====================================== java.lang.NullPointerException: Cannot read field "nullInstanceField" because "<local1>" is null at dustin.examples.npe.NpeDemo.demonstrateFourthExampleAccessInstanceFieldOfNullObject(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) =================== | # 5 : throw null ; | =================== java.lang.NullPointerException: Cannot throw exception because "null" is null at dustin.examples.npe.NpeDemo.demonstrateFifthExampleThrowingConstantNull(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ================================================ | # 6 : Method invocation on null instance field | ================================================ java.lang.NullPointerException: Cannot invoke "String.isEmpty()" because "this.nullInstanceField" is null at dustin.examples.npe.NpeDemo.demonstrateSixthExampleMethodInvocationOnNullInstanceField(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ============================================= | # 7 : synchronized () on null instance field | ============================================= java.lang.NullPointerException: Cannot enter synchronized block because "this.nullInstanceField" is null at dustin.examples.npe.NpeDemo.demonstrateSeventhExampleSynchronizedNullInstanceField(Unknown Source) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ========================================================================== | >>> Null Lost in Long Series of Method Invocations in Single Statement | ========================================================================== java.lang.NullPointerException: Cannot invoke "dustin.examples.npe.DysfunctionalLocation$Province.getCity()" because the return value of "dustin.examples.npe.DysfunctionalLocation$Nation.getProvince()" is null at dustin.examples.npe.NpeDemo.demonstrateNullLostInSeriesOfMethodInvocationsInSingleStatement(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ======================================================= | >>> Null Lost in Dereferenced Constructor Arguments | ======================================================= java.lang.NullPointerException: Cannot invoke "java.lang.Long.longValue()" because "<local6>" is null at dustin.examples.npe.NpeDemo.demonstrateNullLostInConstructorAcceptingMultiplePotentiallyNullArgumentsDereferenced(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) ================================================== | >>> Null Lost in Dereferenced Method Arguments | ================================================== java.lang.NullPointerException: Cannot invoke "java.lang.Long.longValue()" because "<local6>" is null at dustin.examples.npe.NpeDemo.demonstrateNullLostInMethodAcceptingMultiplePotentiallyNullArgumentsDereferenced(Unknown Source) at dustin.examples.npe.NpeDemo.main(Unknown Source) |
JEP 358 explains the use of this flag for seeing additional NullPointerException
details: “The feature can be toggled with the new boolean
command-line option -XX {+|-}ShowCodeDetailsInExceptionMessages
. The option will first have default ‘false
‘ so that the message is not printed. It is intended to enable code details in exception messages by default in a later release.” As we see, this feature is initially turned off by default, but there is a plan to enable the more detailed NullPointerException
messages in the future.
A recent Tweet asked the question, “How it will work if bytecode doesn’t contain variable names?” The question continued by providing a specific example: “Suppose we have code like Object a = ....; a.getName(); //NPE
What kinds of message NPE would have?” Although an example of this is included in my battery of tests shown earlier, I thought I’d provide a more focused example here in response to that question. The next code listing (which is also available on GitHub) shows code adapted from the example used in the Tweet.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package dustin.examples.npe; /** * Simple demonstration to answer Tweet-ed question * "How it will work if bytecode doesn't contain variable names?" */ public class TwoDoubleWhiskeyTweetExample { public static void main( final String[] arguments) { final Person person = null ; person.getName(); //NPE } public static class Person { private String name; public Person( final String newName) { name = newName; } public String getName() { return name; } } } |
The next screen snapshot shows the result of running this simple application with the JDK 14 Early Access Build 20 without and then with the java
launcher flag -XX:+ShowCodeDetailsInExceptionMessages
.
As the screen snapshot indicates, using the -XX:+ShowCodeDetailsInExceptionMessages
flag with the JDK 14 Early Access Build 20 provides this additional detail related to this simple NullPointerException
example: “Cannot invoke “dustin.examples.npe.TwoDoubleWhiskeyTweetExample$Person.getName()” because “<local1>” is null”
An example that is simpler and even closer to the original example provided in the Tweet-ed question is available on GitHub.
JEP 358 (“Helpful NullPointerExceptions“) may not be as flashy as some other JEPs that come to new JDK releases, but it may be one that in the end provides more value to Java developers on a daily basis than some of its flashier peers. There are numerous examples where this will be helpful and many of those example situations are spelled out in the JEP itself and in my code examples referenced in this post.
Published on Java Code Geeks with permission by Dustin Marx, partner at our JCG program. See the original article here: Better NPE Messages in JDK 14 Opinions expressed by Java Code Geeks contributors are their own. |