Kasper complains that it's hard to call methods that always throw. In particular, code like this won't compile because findGetter
is missing a return statement:
public Method findGetter() throws CsvException {
try {
return type.getMethod("get"
+ fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1));
} catch(SecurityException e) {
noSuchGetter(e);
} catch (NoSuchMethodException e) {
noSuchGetter(e);
}
}
public void noSuchGetter(Exception cause) throws CsvException {
String message = String.format("Unable to lookup getter"
+ " for field %s on %s", fieldName, type.getName());
throw new CsvException(message, cause);
}
The problem is that noSuchGetter
always throws, but javac doesn't consider that when building this code. Making that method private
or final
doesn't help -- javac does not look at the body of a method when deciding whether the callers are valid.
Adding return null;
to the end of this method appeases the compiler, but it sure isn't pretty:
public Method findGetter() throws CsvException {
try {
return type.getMethod("get"
+ fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1));
} catch(SecurityException e) {
noSuchGetter(e);
} catch (NoSuchMethodException e) {
noSuchGetter(e);
}
return null; // never executes! (this method never returns null.)
}
As Kasper explains, this workaround sucks! In particular, it suggests to the code reader that he should check for null when calling this method. We need to add a big ugly comment to flag what's going on. Another workaround is to throw AssertionError
s on unreachable lines. This is an improvement but we still have dead code in our program. This line won't ever execute, adding noise to code coverage reports.
Fortunately, there's a clever fix. Declare that noSuchGetter
returns a RuntimeException
and prefix call to it with throw
:
public Method findGetter() throws CsvException {
try {
return type.getMethod("get"
+ fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1));
} catch(SecurityException e) {
throw noSuchGetter(e);
} catch (NoSuchMethodException e) {
throw noSuchGetter(e);
}
}
public CsvException noSuchGetter(Exception cause)
throws CsvException {
String message = String.format("Unable to lookup getter"
+ " for field %s on %s", fieldName, type.getName());
throw new CsvException(message, cause);
}
Now the code reader can see that the noSuchGetter
calls always throw without reading that method's body. And the compiler will complain if you add any code after that call. We can optionally tweak this so noSuchGetter
returns CsvException
rather than throwing it.
Methods that always throw should return what they throw. If the method always throws a RuntimeException
, we'd return that type.