This question was asked in 2020, so the OP has probably figured out what to do by now, but for future generations:
When a function that you call throws an exception, and you do not want to handle it, your only other option is not to rethrow it. Actually, rethrowing it would not buy you anything, because you would still get those "errors at compile time" that you spoke of.
So, here is what is happening:
In Java, there are two kinds of exceptions:
- "checked" exceptions
- "unchecked" exceptions
Unchecked exceptions work exactly as they do in C#: if some function in the call tree throws one, no other function in the call tree needs to be concerned about it until some function high up the tree that catches it and does something meaningful with it. (Remember, "Computer Science" trees are upside down, so "high up the tree" means "near the root".) Obviously, these are not the kind of exceptions that are giving you trouble.
Checked exceptions were a novelty that Java introduced back in the early nineties, and they were admittedly an interesting concept, but looking back now after a few decades we can say with certainty that they did not survive the test of time. Checked exceptions were already being seen by most programmers as an unnecessary hassle even before functional programming started gaining ground, and with the introduction of lambdas into the language, checked exceptions became nothing but an annoying relic of the past, since they are largely incompatible with lambdas. (For more on this, see the top 5 answers to this question: The case against checked exceptions) However, lots of classes that are built into the JDK throw these checked exceptions, so you need to know how to treat them. (I cannot say "how to handle them", because that word already has a very specific meaning in this discussion, and this is not the meaning I want here.)
Checked exceptions are essentially declared exceptions. This means that if a function may throw a certain checked exception, then the function must declare that it does so, in its signature, like this:
void myCoolFunction() throws MyCheckedException { ... }
The Java compiler prohibits checked exceptions from being thrown, or propagating through, functions that do not declare them. In other words:
- If your method may potentially throw a checked exception, then your method must declare in its signature that it does so.
- If your method invokes another method which may potentially throw a checked exception, and your method does not catch and handle that exception, then this is as good as your method potentially also throwing that checked exception, so your method must also declare in its signature that it may throw that exception.
This means that if myCoolerFunction()
invokes myCoolFunction()
, then myCoolerFunction()
has to either catch and handle the exception, or also be declared with throws MyCheckedException
. Note, however, that it does not need to catch it and rethrow it. As a matter of fact, if myCoolerFunction()
rethrows the exception, then it is still obliged to declare in its signature that it throws that exception.
So, finally, the solution to the problem:
Your functions do not have to handle the checked exceptions thrown by functions they call, nor do they have to catch and rethrow anything; all they need to do is declare in their signatures that they also throw such exceptions.
If you find that to be too bureaucratic, you might find it as a consolation to hear that this is the Java way of doing things.
If you still find it too bureaucratic, then the solution you are looking for is conversion of checked exceptions to unchecked exceptions, which is essentially catching and rethrowing that actually works. It is done like this:
void myMethodWithoutCheckedExceptions()
{
try
{
myMethodThatThrowsACheckedException();
}
catch( MyCheckedException e )
{
throw new RuntimeException( e );
}
}
This wraps the checked exception inside a RuntimeException
, which is not a checked exception, and therefore does not need to be declared in the signature of the method.
As Johannes Kuhn hinted at in a comment, it is even possible to avoid the wrapping, but this is really advanced, some would even say hacky stuff: Instead of throw new RuntimeException( e );
you can do throw sneakyException( e );
and then you can have the following method:
@SuppressWarnings( "unchecked" )
public static <T extends Throwable> RuntimeException sneakyException( Throwable t )
throws T
{
throw (T)t;
}
This trick leverages an unchecked type cast to fool the compiler into believing that the exception being thrown is not a checked exception. It works, because checked exceptions are a purely syntactic feature of the Java language, while the runtime does not know anything about them.
Note that my sneakyException()
method which is declared to return a RuntimeException
is better than the void-returning sneakyThrow()
method suggested by Johannes Kuhn because sneakyException()
allows you to use the throw
keyword at the call site, thus indicating to the compiler that program flow will not proceed past that point, whereas sneakyThrow()
does not return an exception that you can throw, therefore you cannot use the throw
keyword at the call site, therefore the compiler might have no knowledge of the fact that sneakyThrow()
never returns.