A method in Java to avoid null pointer exceptions

  • 2020-04-01 03:30:27
  • OfStack

Nobody likes null-pointer exceptions! Is there any way to avoid them? Maybe.

This article discusses the following techniques

1.Optional type (newly introduced in Java 8)
2.Objects class (native to Java 7)

Optional class in Java 8

What is it?

1. New types introduced in Java 8
2. It ACTS as a wrapper around an object of a specified type or in situations where no object exists (null)

Simply put, it's a better alternative to dealing with null values (warning: it may not be obvious at first glance)

Basic usage

It's a type (a class) -- so how do you create an instance of this type?

Just use its three static methods:


public static Optional<String> stringOptional(String input) {
    return Optional.of(input);
}

Simple -- create an Optional wrapper that contains this value. Remember - if this value is null, it will throw an NPE!


public static Optional<String> stringNullableOptional(String input) {
if (!new Random().nextBoolean()) {
input = null;
}
return Optional.ofNullable(input);
}

Personally, I think it's better. There is no risk of NPE -- if the input is null, an empty Optional will be returned.


public static Optional<String> emptyOptional() {
return Optional.empty();
}

If you really want to return an empty value. An "empty" value does not mean null.

Ok, so how do we consume/use Optional?


public static void consumingOptional() {
Optional<String> wrapped = Optional.of("aString");
if (wrapped.isPresent()) {
System.out.println("Got string - " + wrapped.get());
}
else {
System.out.println("Gotcha !");
}
}

The easy way to do this is to check if the Optional wrapper actually has a value (using the isPresent method) -- you'll doubt it's the same as using if(myObj!) = null). Don't worry, I'll explain that.


public static void consumingNullableOptional() {
String input = null;
if (new Random().nextBoolean()) {
input = "iCanBeNull";
}
Optional<String> wrapped = Optional.ofNullable(input);
System.out.println(wrapped.orElse("default"));
}

You can use the orElse method to return a default value in case it is true that it encapsulates a null value - it has obvious benefits. You can avoid the obvious redundancy of calling the ifPresent method when extracting the real value.


public static void consumingEmptyOptional() {
String input = null;
if (new Random().nextBoolean()) {
input = "iCanBeNull";
}
Optional<String> wrapped = Optional.ofNullable(input);
System.out.println(wrapped.orElseGet(
() -> {
return "defaultBySupplier";
}
 
));
}

I'm a little confused about that. Why are there two different approaches to the same purpose? OrElse and orElseGet can be overridden (same name but different parameters).

However, the obvious difference between the two methods is their arguments -- you can do this using a lambda expression instead of an instance of Supplier (a functional interface)

Why is using Optional better than the usual null check?

One of the great benefits of using Optional is that you can state your intent more clearly -- returning a null value will cause consumers to wonder if this is an intentional return, so you'll have to check with javadoc to find out. And Optional is pretty straightforward.

With Optional, you can avoid NPE altogether -- as mentioned above, using options. ofNullable, orElse, and orElseGet can keep us out of NPE.

Another savior!

Take a look at this code snippet


package com.abhirockzz.wordpress.npesaviors;
 
import java.util.Map;
import java.util.Objects;
 
public class UsingObjects {
 
String getVal(Map<String, String> aMap, String key) {
return aMap.containsKey(key) ? aMap.get(key) : null;
}
 
public static void main(String[] args) {
UsingObjects obj = new UsingObjects();
obj.getVal(null, "dummy");
}
}

Which one might be empty?

1. The Map object
2. Key used for search
3. This instance of the method invocation

If an NPE is thrown, how can we determine which is null?


package com.abhirockzz.wordpress.npesaviors;
 
import java.util.Map;
import java.util.Objects;
 
public class UsingObjects {
String getValSafe(Map<String, String> aMap, String key) {
Map<String, String> safeMap = Objects.requireNonNull(aMap,
"Map is null");
String safeKey = Objects.requireNonNull(key, "Key is null");
 
return safeMap.containsKey(safeKey) ? safeMap.get(safeKey) : null;
}
 
public static void main(String[] args) {
UsingObjects obj = new UsingObjects();
obj.getValSafe(null, "dummy");
}
}

RequireNonNull method

1. Returns an object if it is not null
2. If the value is null, the returned NPE will come with the specified message

Why is it better than if(myObj! = null) is better?

The stack trace you see will clearly see the method call to objects.requirenonnull. This, combined with your own error log, will allow you to locate problems more quickly... Faster, at least in my opinion.

You can also use a self-validator, such as a simple validator to make sure there are no null values.


import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
 
public class RandomGist {
 
    public static <T> T requireNonEmpty(T object, Predicate<T> predicate, String msgToCaller){
        Objects.requireNonNull(object);
        Objects.requireNonNull(predicate);
        if (predicate.test(object)){
            throw new IllegalArgumentException(msgToCaller);
        }
        return object;
    }
 
    public static void main(String[] args) {
       
    //Usage 1: an empty string (intentional)
 
    String s = "";
    System.out.println(requireNonEmpty(Objects.requireNonNull(s), (s1) -> s1.isEmpty() , "My String is Empty!"));
 
    //Usage 2: an empty List (intentional)
    List list =  Collections.emptyList();
    System.out.println(requireNonEmpty(Objects.requireNonNull(list), (l) -> l.isEmpty(), "List is Empty!").size());
 
    //Usage 3: an empty User (intentional)
    User user = new User("");
    System.out.println(requireNonEmpty(Objects.requireNonNull(user), (u) -> u.getName().isEmpty(), "User is Empty!"));
}
 
    private static class User {
        private String name;
 
        public User(String name){
            this.name = name;
        }
 
        public String getName(){
            return name;
        }
    }
}

Don't let NPE become a pain in the wrong places. We have many tools to better deal with NPE and even eradicate it completely!


Related articles: