Atom Feed SITE FEED   ADD TO GOOGLE READER

A simple pattern for avoiding NullPointerExceptions

Imagine you've got some method that sometimes returns a legitimate value, and other times returns the special value null 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();
}
Without getting into the tired old "checked vs. unchecked" exceptions debate, it nevertheless seems odd to throw an IllegalArgumentException when there was in fact nothing wrong with the argument itself (here, the Address) -- it seems to violate the semantic intent.

I would recommend throwing a custom exception such as StoreNotFound or NoSuchStore, which should probably be derived from a more general ObjectNotFound | NoSuchObject | NoMatchingEntity base.