Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

NPE-Free Code Without Null Checks

DZone's Guide to

NPE-Free Code Without Null Checks

Optionals provide a great way to avoid null checks. See how you can wrap your code with Optionals to handle values that might or might not be null.

· Java Zone
Free Resource

Build vs Buy a Data Quality Solution: Which is Best for You? Gain insights on a hybrid approach. Download white paper now!

Image title

Is this how your Java code review starts? NPE is always a nightmare for a Java developer. Let's jump to an unusual code snippet:

public interface Service {  
    public boolean switchOn(int timmer);
    public boolean switchOff(int timmer);
    //Other controls 
}
public class RefrigeratorService implements Service {
// ...
}
public class HomeServices {
    private static final int NOW = 0;
    private static HomeServices service;

    public static HomeServices get() {
        //Null Check #1
        if(service == null) {
            service = new HomeServices();
        }
        return service;
    }

    public Service getRefrigertorControl() {
        return new RefrigeratorService();
    }

    public static void main(String[] args) {
        /* Get Home Services handle */
        HomeServices homeServices = HomeServices.get();
        //Null Check #2
        if(homeServices != null) {
            Service refrigertorControl = homeServices.getRefrigertorControl();
            //Null Check #3
            if (refrigertorControl != null) {
                refrigertorControl.switchOn(NOW);
            }
        }
    }
}


As you can see above, there are multiple null checks in the code. Of course, that's to make it robust against all possible cases. That is necessary, but...

Is There a Better Way?

Well, yes! Java 8 introduced java.util.Optional<T>. It is a container that may or may not hold non-null values. Java 8 gave us a safer way to handle objects whose value may be null in some of the cases. It is inspired from the ideas of Haskell and Scala. 

In a nutshell, the Optional class includes methods to explicitly deal with the cases where a value is present or absent. However, the advantage compared to null references is that the Optional<T> class forces you to think about the case when a value is not present. As a consequence, you can prevent unintended null pointer exceptions.

In the example above, we have a home service factory that handles multiple appliances available in the home. But these services may or may not available/functional. That means it may result in a NullPointerException. Instead of adding a null if condition before using any service, let's wrap it in Optional<Service>.

Wrapping in Option<T>

Let's consider a method to get the reference of the service from the factory. Instead of returning the service reference, wrap it with Optional. It lets the API user know that the returned service may or may not be available/functional. Use it defensively: 

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}


As you can see, Optional.ofNullable() provides an easy way to get the reference wrapped. There are other ways to get references of Optionals — either Optional.empty() or Optional.of(). One returns empty objects instead of returning null, and the other wraps non-nullable objects, respectively.

How Does This Help Avoid Null Checks?

Once you have wrapped the reference object, Optional provides many useful methods to invoke methods on wrapped references without NPEs.

Optional ref = homeServices.getRefrigertorControl(); 
ref.ifPresent(HomeServices::switchItOn);


Optional.ifPresent invokes the given Consumer with a reference if it is a non-null value. Otherwise, it does nothing. 

@FunctionalInterface 
public interface Consumer<T> 
This represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects.

It is so clean and easy to understand. In the above code example, HomeService.switchOn(Service) gets invoked if the Optional holding reference is non-null.

We use the ternary operator often for checking for null conditions and returning alternative or default values. Optional provides another way to handle the same condition without checking null. Optional.orElse(defaultObj) returns defaultObj if the Optional has a null value. Let's use this in our sample code:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}


Now HomeServices.get() does the same thing but in a better way. It checks whether the service is already initialized. If it is, then it returns it or creates a new New service. Optional<T>.orElse(T) helps return the default value.

Finally here is our NPE- and null-check-free code:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

    public static Optional<HomeServices> get() {
        service = Optional.of(service.orElse(new HomeServices()));
        return service;
    }

    public Optional<Service> getRefrigertorControl() {
		//Service Discovery for Refrigerator 
		if(ServiceDiscovery.isAvaiable("refrigetor")) {
			Service s = RefrigeratorService.get();
			return Optional.ofNullable(s);
		}
		//Possible that the service is not avaiable
		return Optional.empty();
	}

    public static void main(String[] args) {
        /* Get Home Services handle */
        Optional<HomeServices> homeServices = HomeServices.get();
        if(homeServices.isPresent()) {
            Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
            refrigertorControl.ifPresent(HomeServices::switchItOn);
        }
    }

    public static void switchItOn(Service s){
        //...
    }
} 


Build vs Buy a Data Quality Solution: Which is Best for You? Maintaining high quality data is essential for operational efficiency, meaningful analytics and good long-term customer relationships. But, when dealing with multiple sources of data, data quality becomes complex, so you need to know when you should build a custom data quality tools effort over canned solutions. Download our whitepaper for more insights into a hybrid approach.

Topics:
java 8 ,optional ,npe ,java ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}