I have two simple methods:
public void proceedWhenError() {
Throwable exception = serviceUp();
if (exception == null) {
// do stuff
} else {
logger.debug("Exception happened, but it's alright.", exception)
// do stuff
}
}
public void doNotProceedWhenError() {
Throwable exception = serviceUp();
if (exception == null) {
// do stuff
} else {
// do stuff
throw new IllegalStateException("Oh, we cannot proceed. The service is not up.", exception)
}
}
The third method is a private helper method:
private Throwable serviceUp() {
try {
service.connect();
return null;
catch(Exception e) {
return e;
}
}
We had a small talk with a colleague of mine about the pattern used here:
returning an Exception
(or Throwable
) object from the serviceUp()
method.
The first opinion:
It is an anti-pattern to use Exceptions to control the workflow and we should only return boolean from
serviceUp()
and never the Exception object itself. The argument is
that using Exceptions to control the workflow is an anti-pattern.
The second opinion:
It is alright, as we need to deal with the object afterwards in the
two first methods differently and whether returning Exception object
or boolean does not change the workflow at all
Do you think 1) or 2) is correct and especially, why?
Note, that the question is ONLY about the method serviceUp()
and its return type – boolean
vs Exception
object.
Note: I am not questioning whether to use Throwable or Exception objects.
Best Answer
It is an anti-pattern to use exceptions to direct the flow only when the exception is thrown in a non-exceptional situation*. For example, ending a loop by throwing an exception when you reach the end of a collection is an anti-pattern.
Controlling the flow with actual exceptions, on the other hand, is a good application of exceptions. If your method encounters an exceptional situation that it cannot handle, it should throw an exception, thus re-directing the flow in the caller to the exception handler block.
Returning a "naked"
Exception
object from a method, rather than throwing it, is certainly counter-intuitive. If you need to communicate the results of an operation to the caller, a better approach is to use a status object that wraps all the relevant information, including the exception:Now the caller would receive
CallStatus
fromserviceUp
:Note that the constructor is private, so
serviceUp
would either returnCallStatus.SUCCESS
or callCallStatus.error(myException)
.* What is exceptional and what is not exceptional depends a great deal on the context. For example, non-numeric data causes an exception in
Scanner
'snextInt
, because it considers such data invalid. However, the same exact data does not cause an exception inhasNextInt
method, because it is perfectly valid.