A simple pattern for avoiding NullPointerExceptions
Imagine you've got some method that sometimes returns a legitimate value, and other times returns the special valuenull
to indicate that no legitimate value exists:public interface StoreLocator {
/**
* Returns a store nearest to {@code address}, or {@code null}
* if there are no stores within delivery range of the address.
*/
PizzaStore findNearbyStore(Address address);
}
Why returning null is annoying
Since it can return
null
, we need to check for that whenever we call the method: public Receipt createReceipt(Order order) {
...
PizzaStore closestLocation
= storeLocator.findNearbyStore(order.getDeliveryAddress());
if(closestLocation == null) {
throw new IllegalArgumentException("No stores nearby!");
}
if (!closestLocation.equals(order.getStoreAddress())) {
receipt.addMessage("Next time you order a pizza, consider"
+ " our closer %s location", closestLocation);
}
return receipt;
}
Why returning null is dangerous
In another scenario we might forget to check for null, and end up creating corrupt data. The invalid null value might not be discovered for a long time - it might even get persisted in your database. For example:
public Order createOrder(Address toAddress, List<LineItem> lineItems) {
PizzaStore store = storeLocator.findNearbyStore(toAddress);
return new Order(toAddress, lineItems, store);
}
A simple fix - two methods
Provide two methods to cover the two interesting cases: where there's always a legitimate value, and where there's only sometimes a legitimate value:
public interface StoreLocator {
/**
* Returns a store nearest to {@code address}.
* @throws IllegalArgumentException if there are no stores
* within delivery range of the address.
*/
PizzaStore findNearbyStore(Address address);
/**
* Returns a store nearest to {@code address}, or {@code defaultValue}
* if there are no stores within delivery range of the address.
*/
PizzaStore findNearbyStore(Address address, PizzaStore defaultValue);
}
It's simple and it makes for a user-friendly API. It's particularly nice when the defaultValue fits the problem neatly:
public Address getCustomerServiceAddress(Address customerAddress) {
return storeLocator.findNearbyStore(
customerAddress, PizzaStores.HEAD_OFFICE).getAddress();
}