Keep Java checked exceptions in a bounded context
One of the features that I dislike in Java compared to C# is checked exceptions.
For me an exception is about a situation which is exceptional, and if we know that there is a possibility of it happening and even have that possibility defined in our code then it doesn’t seem all that exceptional to me.
Having said that they do at least provide information which you can’t help but notice about what can go wrong when you make a call to a particular method.
The problem is that often these checked exceptions just get passed on - i.e. not handled - until we end up with an exception on the page the user sees which is completely irrelevant to the action they are trying to undertake.
To give an example, we have been using the OGNL library to hydrate some objects for testing using the builder pattern.
We have something like this:
public class FooBuilder {
private String bar;
public FooBuilder setBar(String bar) {
this.bar = bar;
return this;
}
public Foo toFoo() {
Foo foo = new Foo();
setValue(foo, "bar", bar);
return foo;
}
protected void setValue(Object object, String propertyName, Object propertyValue) {
try {
OgnlWrapper.setValue(object, propertyName, propertyValue);
} catch (OgnlException e) {
throw new RuntimeException(e);
}
}
}
import ognl.DefaultMemberAccess;
import ognl.MemberAccess;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
public class OgnlWrapper {
public static void setValue(Object object, String propertyName, Object propertyValue) throws OgnlException {
Ognl.setValue(propertyName, createOgnlContext(), object, propertyValue);
}
private static OgnlContext createOgnlContext() {
MemberAccess memberAccess = new DefaultMemberAccess(true);
OgnlContext ognlContext = new OgnlContext();
ognlContext.setMemberAccess(memberAccess);
return ognlContext;
}
}
We can then build an instance of 'Foo' like so:
Foo foo = new FooBuilder().setBar("barValue").toFoo();
What is interesting here is not the OGNL library in itself but the checked 'OgnlException' which the 'Ognl.setValue(…)' method defines.
If I am using the FooBuilder I don’t care how the Foo object is created, all I care is that I get it. Therefore we don’t want to bubble the implementation details of how we are creating the object upwards.
I only care about the OgnlException if I am calling the OgnlWrapper and therefore that is where the exception should be caught and then rethrown as a Runtime exception.
I like to refer to this area of OgnlWrapper callees as being a bounded context - that exception should only be applicable in that particular area and beyond that it should not exist.
Doing this allows us more flexibility around the way we implement things. If I decide in the future to use a different library instead of OGNL to do the same job I don’t need to worry that the callees of FooBuilder will all need to be updated. I can just make the change inside FooBuilder and that’s it!
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.