Finally, a use for checked exceptions

Those of you who have followed my blog, been subjected to it on a client site or have talked to me about programming will know that I have a few issues with checked exceptions.
Well, this morning I had an idea of a potentially powerful use of checked exceptions (if only temporarily).

The same people who will have heard about my dislike of checked exceptions will probably also know about my dislike of returning null. I was mulling over a legacy code base that I may be taking over in the future and one of the first things that I would like to do is to replace all null returns with something meaningful.

Normally, I would remove nulls by searching for the offending return statement, fixing it to do something meaningful (and if I don’t know what meaningful means in this place, throw an exception), then find usages of that method in the workspace and remove any (now defunct) null checks.

Enter the checked exception as a refactoring tool!


public Result someMethod() {
Result result = someMethodIDontControl();
if(null == result) {return null;} // Actually, this is really crap because in reality, I'd just return result, but bear with me!
return result;
}

becomes


public Result someMethod() throws IUsedToReturnNullPleaseFixMe {
Result result = someMethodIDontControl();
if(null == result) {throw new IUsedToReturnNullPleaseFixMe();} // This is temporary. The right thing _might_ be an exception, but we'll work that out later
return result;
}

Now we can lean on the compiler to find all the places that call this method. We can strip out any null checks (that are now unnecessary), look to see if there is something meaningful that we should be doing, and then add throws IUsedToReturnNullPleaseFixMe to the method we’ve just fixed. When we’ve propagated our exception to the top level, we’re done. What I would do at this point is probably delete the exception class, fix all the resulting compile errors by removing the throws clauses, until finally the last compilation error is the original throw new IUsedToReturnNullPleaseFixMe(); which I can now replace with something meaningful (e.g. throw a meaningful exception, return a meaningful NullResult, convert to a collection and return emptySet() etc)

  • Anonymous

    Great invention! Will have to tryout now.

  • http://twitter.com/piotrbuda Piotr Buda

    How is this actually different to ‘find occurences’ that most IDEs have?

  • http://andypalmer.com Andy Palmer

    With find occurrences, we have to manually keep track of where we are propagating things through.
    For example, if my null returning method is called method1 and it’s used in five places, then I can find those five places easily. But… I then have to find where those five places are used, and if they’re each used in five places, that’s twenty five places… I have a tendency to get lost really easily when things start to pile up like that.
    Using this method, the first five places will go red because they won’t compile, because of the checked exception, and then the next twenty five places will go red because of the checked exception. I can’t get lost now, and when the IDE is no longer red, I can remove the checked exception and know that I’ve removed everything related to that null.

    If you have a small, well-factored code base, this is not necessary. When you are new to a legacy code base, this method is a bit like the marker dyes that they use in medicine to see just how deeply ingrained the null problem actually is.

  • http://www.facebook.com/people/Alex-Shneyderman/100003113307512 Alex Shneyderman

    I do not really see the point either. Alt+F7 in IDEA gives you all the usage. The only advantage of the method described in the post is that you really can not compile until you fix all the usage places. In case of search this “automatic” check is harder to ensure and you might miss a place or two. 

  • http://andypalmer.com Andy Palmer

    Exactly. That and the fact that (IIRC) Alt+F7 only goes one level deep. This method encourages us to do the right thing all the way up the call stack.

  • http://twitter.com/ysrthgrathe Thomas Kriegelstein

    Changing an API and relying on the compiler to highlight the downstream changes is so clever. I might not have thought of that several years, like 10, before. Anyways kudos for sharing that stuff. And it works with your favorite IDE, e.g. Emacs, Notepad and vi too.

  • http://www.facebook.com/people/Alex-Shneyderman/100003113307512 Alex Shneyderman

    Hmm, I do not understand the second part of the explanation. The first five markers is all you need I would think. The rest is noise – although I do not understand why/how out of five usage places you would get another 20. If I have a method cal chain like so:
    someUsageOfUsage -> usage -> npeProblem(this is the method where we add new Exception)I would only get trouble reports with the last portion of the chain (5 usages) pointing to the usage methods. The rest is noise, but I think compiler would not even complain aout the first link in the chain) and I woud not like to deal with that at all. So, I do not really see transitive use here. How?

    Of course, it would be worth mentioning that the limitation of the trick is the loose coupling (reflective calls, JSP pages, etc). This method fails, and would only give the person using it a false sense of understanding.  

  • http://andypalmer.com Andy Palmer

    If my someMethod() from above was used in another method like so:
    public Result someOtherMethod() {
      if (someValue) {return something;}
      return someMethod();
    }

    then the value of someOtherMethod can also be null. So I also need to check the callers of that method, and so on, until I reach someone who either knows what the sensible thing to do with the value is or, I reach the extremities of the program (for example, the main loop or the request handler).

    In the simple case, there isn’t a need to do this, but removing a null return should encompass the entire system, and this method ensures that this is the case.

    You are correct about reflective calls, but I would hope that people using reflective calls were aware of the power (and therefore, danger) of that approach. 

  • http://profiles.google.com/ricky.clarkson Ricky Clarkson

    You’ll need to remove all the catch (Exception e) and catch (Throwable t) instances that inevitably end up in large code bases precisely because of checked exceptions.

  • Dileep Mandapam

    I don’t see any other advantage of this refactoring technique , other than finding usages of “someMethod”

  • http://andypalmer.com Andy Palmer

    The advantage is that it highlights the direct and indirect usages of someMethod. Direct usages is obviously trivial, it’s the indirect usages where the bugs usually lie.

  • http://andypalmer.com Andy Palmer

    Yes, although the advantage of this method is that it propagates _beyond the direct usages_.

  • http://andypalmer.com Andy Palmer

    Good point. Although I would hope (for my sanity) that if there are catch everything blocks in the code, that they’re doing something sensible and I don’t need to go and change them at this point.
    (I’ll probably go and rip them out at the soonest opportunity though :-) )

  • Demian Alonso

    Just a little contribution: Many ideas do have a way to search for references hierarchically. I now for sure that in Eclipse is called “call hierarchy”, but I am quite sure that on Idea is possible too, but I do not know the shortcuts :)

    Still, the “it won’t compile”  technic does prevent you from missing any case.

  • http://andypalmer.com Andy Palmer

    Good point. I have used Call Hierarchy, although when I read your comment, the idea that popped into my mind was ”debugging”. 
    I use “Find references in workspace (<ctrl><shift>g)” a lot while refactoring (particularly while deleting code), but not Call Hierarchy. I’m not sure why; I’ll investigate that feeling next time I’m doing something like this.

    Also, yes, the point of this is to leverage the compiler to help make sure we’ve removed all traces of the original null. I hate null checks within code I control, as that is indicative of a bad citizen (see Good Citizen)