The example you cite is due to poor API design (there is no clean way to check whether a String is a valid integer except trying to parse it and catching the exception).
At the technical level, throw and try/catch are control flow constructs that allow you to jump up the call stack, nothing more and nothing less. Jumping the up the call stack implicitly connects code that is not close together in the source, which is bad for maintainability. So it should only be used when you need to do that and the alternatives are even worse. The widely accepted case where the alternatives are worse is error handling (special return codes that need to be checked and passed up each level of the call stack manually).
If you have a case where the alternatives are worse (and you really have considered all of them carefully), then I'd say using throw and try/catch for control flow is fine. Dogma is not a good substitute for judgement.
The problem isn't the local catch block, the problem is the log and rethrow. Either handle the exception or wrap it with a new exception that adds additional context and throw that. Otherwise you will run into several duplicate log entries for the same exception.
The idea here is to enhance the ability to debug your application.
Example #1: Handle it
try
{
doSomething();
}
catch (Exception e)
{
log.Info("Couldn't do something", e);
doSomethingElse();
}
If you handle the exception, you can easily downgrade the importance of the exception log entry and there is no reason to percolate that exception up the chain. It's already dealt with.
Handling an exception can include informing users that a problem happened, logging the event, or simply ignoring it.
NOTE: if you intentionally ignore an exception I recommend providing a comment in the empty catch clause that clearly indicates why. This lets future maintainers know that it was not a mistake or lazy programming. Example:
try
{
context.DrawLine(x1,y1, x2,y2);
}
catch (OutOfMemoryException)
{
// WinForms throws OutOfMemory if the figure you are attempting to
// draw takes up less than one pixel (true story)
}
Example #2: Add additional context and throw
try
{
doSomething(line);
}
catch (Exception e)
{
throw new MyApplicationException(filename, line, e);
}
Adding additional context (like the line number and file name in parsing code) can help enhance the ability to debug input files--assuming the problem is there. This is kind of a special case, so re-wrapping an exception in an "ApplicationException" just to rebrand it doesn't help you debug. Make sure you add additional information.
Example #3: Don't do anything with the exception
try
{
doSomething();
}
finally
{
// cleanup resources but let the exception percolate
}
In this final case, you just allow the exception to leave without touching it. The exception handler at the outermost layer can handle the logging. The finally
clause is used to make sure any resources needed by your method are cleaned up, but this is not the place to log that the exception was thrown.
Best Answer
Your
parsedDate
method doesn't need thethrows Exception
clause because the exception is already being caught.The compiler looks at the method signature when a method is called and sees
throws Exception
, so it expects you to handle it.