1

Suppose we have a function that is deep in the call stack. This function does multiple operations and it calls 5 different functions that throw exceptions.

In C# I could leave these exceptions and be sure that if any of them is thrown then the program breaks. This is what I was hoping for here - these exceptions should stop the program. In Java I get errors at compile time. As far as I know I have 2 options:

  1. Handle all of them and keep the program running. But as I said - I do want to break program and handling all of the exceptions might only make a mess in the code.
  2. Make a function rethrow these exceptions. However if I did that so deep in the call stack I would need to make rethrows on every other level. That sounds like a really bad design.

To clear things out - I don't mean that I want to ignore every single exception. Many of them are logged etc. but ability to just leave an exception and be sure to get detailed error when a program breaks is very convenient in C#.

What can I do about that in Java?

Rasmond
  • 442
  • 4
  • 15
  • Even in C# unhandled exceptions are almost always a bad idea. If you don't want to handle them in the function that the exception occurs in, you should throw the exception up the stack and your top level function should handle the error by calling exit on the application or whatever is applicable to what you are doing. Just letting an exception kill your app is not really a good practice. – user1289451 Apr 16 '20 at 15:15

2 Answers2

1

In Java, you must handle checked exceptions, which is why you are seeing errors. This is enforced by the compiler. How you handle checked exceptions depends on the use-case.

The intention at the language level was probably that these exceptions could be recovered from. For instance, IO APIs typically throw checked exceptions.

If you want the application to crash, you need to catch and rethrow these exceptions or declare them with a throws. e.g.

void doStuff(String name) throws FileNotFoundException {
  BufferedInputStream bis = new BufferedInputStream(new FileInputStream(name));
  .... do stuff
}

Alternately, you can re-throw the exception and wrap it in an unchecked exception:

void doStuff(String name) {
  BufferedInputStream bis;
  try {
    bis = new BufferedInputStream(new FileInputStream(name));
  } catch(FileNotFoundException e) {
    throw new RuntimeException(e); 
  }
  .... do stuff
}

As I said earlier, however, this is enforced by the compiler and, as a result, there is another way around it.

Lombok has a @SneakyThrows annotation which bypasses the compiler check.

Christopher Schneider
  • 3,745
  • 2
  • 24
  • 38
0

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:

  1. "checked" exceptions
  2. "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:

  1. If your method may potentially throw a checked exception, then your method must declare in its signature that it does so.
  2. 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.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142