Categories
Programming

Learning to Like Exceptions

If you would have told me two years ago that I would being writing Java for my livelihood I would have punched you.

Transitioning from more dynamically typed environments to Java felt like I was being bossed around by javac and I hated it. The most tedious example of this was exception handling. A few projects and library later I’ve learned to love them.

Your Methods Lie

Look at any Android project’s source code you’re going to see source code riddled with null checks like this:

Object thing = mWidget.getThing();

if (thing != null) {
  thing.doSomething();
}

The problem is mWidget.getThing() lied to us. It says it returns Object but it in fact can return nothing or in Java: null1.

Usually the null check exists because at some point calling thing.doSomething() caused a NullPointerException and some poor user experienced a crashing app.

In Java you can’t completely avoid null checks but you can do things to avoid exacerbating the problem.

Don’t be Afraid to Throw

In my efforts to make things easier I find that many times I create a utility method that wraps another “exception happy” method (like working with io).

public class UploadUtil {

  public static Uri uploadImage(Bitmap image) {
    try {
        String filename = BitmapUtil.generateUniqueJpgFilename(filenamePrefix);
        File file = BitmapUtil.saveBitmapAsJpg(photo, Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), filename);

        return Uri.fromFile(file);
    } catch (IOException exception) {
      return null;
    }
  }

}

Pretty simple to follow: provide a Bitmap get back a Uri and do something with the Uri:

Uri uri = UploadUtil.uploadImage(myBitmap);

Request request = new StreamFileUploadRequest(uri);

But now whenever I use the UploadUtil.uploadImage(Bitmap) method I have to remember to do a null check every time.

If another developer (e.g. my future self) were to use this method they probably won’t know to do a null check unless:

  1. They have access to the source code and read it
  2. The null case is documented
  3. They read the documentation

I thought I was saving myself some effort by handling the exception in one place, but it’s now even worse because instead of a compile time error this has a high chance of not being discovered until a NullPointerException crash.

Nothing is Wrong

The method signature of UploadUtil.uploadImage(Bitmap) says it returns a Uri, so let’s make sure we’re not lying anymore by also returning nothing:

public class UploadUtil {

  public static Uri uploadImage(Bitmap image)
  throws IOException {
    String filename = BitmapUtil.generateUniqueJpgFilename(filenamePrefix);
    File file = BitmapUtil.saveBitmapAsJpg(photo, Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), filename);

    return Uri.fromFile(file);
  }

}

That’s easy, just throw the exception. Now the user of uploadImage(Bitmap) will have an explicit code path for handling this:

try {
  Uri uri = UploadUtil.uploadImage(myBitmap);
  Request request = new StreamFileUploadRequest(uri);
} catch (IOException exception) {
  Log.e(TAG, "Failed to upload", e);
  notifyUser(exception);
}

Another benefit is the compiler will now point out all the places you should have been checking for null.

Be Nice to Others

I used to hate using methods that threw exceptions because of the try/catch dance but I have learned that a thrown Exception is another developer looking out for me.

I have some general guidelines for myself now concerning exceptions:

  1. Instead of returning null throw an Exception if it makes sense.
  2. Wait to catch exceptions at the highest level possible. Ideally when the software is directly interacting with the user.

Exceptions can feel heavy handed and don’t make sense in every case so I’m not dogmatic about these guidelines. If you’re doing Android development using Android Studio then take a look at Nullable annotations.


  1. One exciting thing about Swift is that it solves this problem with optionals.↪︎