Java 14 - Helpful NullPointerExceptions (JEP 358)

One of the feature in Java 14, JEP 358 – Helpful NullPointerExceptions aims to help developers to point-out the cause of a Null Pointer Exception.

As example, we have two classes Customer and Address:

Customer.java
package com.dariawan.jdk14.dto;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Customer {
 
    private Integer id;
    private String name;
    private Address address;
}
                    

Address.java
package com.dariawan.jdk14.dto;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Address {
    
    private String addressLine1;
    private String addressLine2;
    private String addressLine3;
    private String zipCode;
}
                    

Now let’s take a look at JEP358NullPointerExample01, when the program try to access the value of cust.getAddress().getAddressLine1():

package com.dariawan.jdk14;

import com.dariawan.jdk14.dto.Customer;

public class JEP358NullPointerExample01 {
    
    public static void main(String[] args) {
        Customer cust = new Customer();
        cust.setId(1);
        cust.setName("Clark Kent");
        
        System.out.println(cust);
        System.out.println(cust.getAddress().getAddressLine1());
    }
}
                    

When running the program above, you will get following result:

$ java com.dariawan.jdk14.JEP358NullPointerExample01
Customer(id=1, name=Clark Kent, address=null)
Exception in thread "main" java.lang.NullPointerException
        at com.dariawan.jdk14.JEP358NullPointerExample01.main(JEP358NullPointerExample01.java:51)

Java 14 try to help developer to understanding what causing NullPointerException by showing precisely which variable was null:

$ java --enable-preview -XX:+ShowCodeDetailsInExceptionMessages com.dariawan.jdk14.JEP358NullPointerExample01
Customer(id=1, name=Clark Kent, address=null)
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.dariawan.jdk14.dto.Address.getAddressLine1()" because the return value of "com.dariawan.jdk14.dto.Customer.getAddress()" is null
        at com.dariawan.jdk14.JEP358NullPointerExample01.main(JEP358NullPointerExample01.java:51)

This feature is disabled by default, and can be enabled with the option: -XX:+ShowCodeDetailsInExceptionMessages.

More Examples

Here some examples of the error messages before and after this feature is enabled:

JEP358NullPointerExample02.java
package com.dariawan.jdk14;

import java.util.List;

public class JEP358NullPointerExample02 {
    
    public static void main(String[] args) {
        List<String> names = null;
        System.out.print(names.size());
    }
}
                    

Before:

$ java com.dariawan.jdk14.JEP358NullPointerExample02
Exception in thread "main" java.lang.NullPointerException
        at com.dariawan.jdk14.JEP358NullPointerExample02.main(JEP358NullPointerExample02.java:47)

After:

$ java --enable-preview -XX:+ShowCodeDetailsInExceptionMessages com.dariawan.jdk14.JEP358NullPointerExample02
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.List.size()" because "names" is null
        at com.dariawan.jdk14.JEP358NullPointerExample02.main(JEP358NullPointerExample02.java:47)

And following example is often happen when we compare two variables:

JEP358NullPointerExample03.java
package com.dariawan.jdk14;

import java.util.HashMap;
import java.util.Map;

public class JEP358NullPointerExample03 {
    
    public static void main(String[] args) {
        Map<String, String> maps = new HashMap<>();
        maps.put("key1", "value1");
        
        String key = maps.get("key2"); // somehow is null
        if (key.equals("key1")) {            
            System.out.print("key equals key1");
        }
    }
}
                    

Of course, the comparison line is better if it re-written as if ("key1".equals(key")) , regardless... here the result before:

$ java com.dariawan.jdk14.JEP358NullPointerExample03
Exception in thread "main" java.lang.NullPointerException
        at com.dariawan.jdk14.JEP358NullPointerExample03.main(JEP358NullPointerExample03.java:51)

And after:

$ java --enable-preview -XX:+ShowCodeDetailsInExceptionMessages com.dariawan.jdk14.JEP358NullPointerExample03
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because "key" is null
        at com.dariawan.jdk14.JEP358NullPointerExample03.main(JEP358NullPointerExample03.java:51)

Conclusion

From the three examples above, the error message before this enhancement is unclear, and need more tracing even guessing to get the exact culprit. JEP 358 bring a feature that can save developer's "precious" time to trace and understand why their program and logic is gone wrong.