Friday, 29 April 2011

Dealing with Null Return Values

One of the things most of us do is to return null from their methods without questioning the validity of the technique. It should be widely excepted, but isn’t, that returning null is a bad idea as it forces any and all client code to constantly check method return values and take special action when they are null. Not only does your client code need to constantly check for null there is also the increased risk of a NullPointerException as it’s easy to miss a out a check altogether, which from experience, is a common occurrence.

There are two approaches you can take instead of returning null and these are:

This blog talks about using exceptions, and in this case I’m talking about checked exceptions, where the exception is used as a return type forcing your client code to take notice. I’ll try to explain what I’m talking about by using the usual contrived scenario of the type that you find in most programming books. The example I'll use is that of the ubiquitous employee details stored in a database and retrieved by some client code in the form of an Employee object using an EmployeeDAO.


Given that any and all our method signatures can return null, then some sample client code would look something like this:

    EmployeeDAO instance = new EmployeeDAO();

   
// Get an existing employee and print the phone number
   
Employee employee = instance.getEmployeeByName("John", "Smith");
   
if ((employee != null) && (employee.getPhoneNumber() != null)) {
     
System.out.println("John Smith's phone number is:" + employee.getPhoneNumber());
   
}

   
// The same thing will happen if we ask for a list of employees by surname.
    // We need to check for null
   
List<Employee> employees = instance.getEmployeesBySurname("Parker");
   
if (employees != null) {
     
for (Employee item : employees) {
       
if ((employee.getPhoneNumber() != null)) {
         
System.out.println(item.getFirstName() + " " + item.getSurname()
             
+ "'s phone number is:" + item.getPhoneNumber());
       
}
      }
    }

   
// Whoops we've forgotten the null check
   
employee = instance.getEmployeeByName("Wil", "Johnson");
    System.out.println
("Wil Johnson's phone number is:" + employee.getPhoneNumber());

… and ALL clients would have to do these checks.

Using checked exceptions, we can tidy this code up somewhat by firstly changing our method signatures to throw exceptions

  public Employee getEmployeeByName(String firstName, String surname)
      throws
NoDataAvailableException;
 
 
public List<Employee> getEmployeesBySurname(String surname)
      throws
NoDataAvailableException ; 

...and then simply adding a try / catch block to our client code:

    try {
     
EmployeeDAO instance = new EmployeeDAO();

     
// Get an existing employee and print the phone number
     
Employee employee = instance.getEmployeeByName("John", "Smith");
      System.out.println
("John Smith's phone number is:" + employee.getPhoneNumber());

     
// Try to get all employees with the surname 'Parker'
     
List<Employee> employees = instance.getEmployeesBySurname("Parker");
     
for (Employee item : employees) {
       
System.out.println(item.getFirstName() + " " + item.getSurname()
           
+ "'s phone number is:" + item.getPhoneNumber());
     
}

    }
catch (NoDataAvailableException e) {
     
e.printStackTrace();
   
}

This obviously looks better than the first sample code, but the thing to note is that the general theme here is to force the Employee objects to do more work, which centralises responsibility for null, or special case checking in one location; hence, improving code quality and reducing the possibility of errors.

But, is using exceptions the best you can do? My next blog will talk about implementing the same scenario using Special Case Pattern, which to me is the best way of approaching this problem...

No comments: