Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it legal to re-throw a Throwable in certain cases, without declaring it?

Tags:

I would expect the following code to raise a compile-time error on throw t;, because main is not declared to throw Throwable, but it compiles successfully (in Java 1.7.0_45), and produces the output you would expect it to if that compile-time error was fixed.

public class Test {     public static void main(String[] args) {         try {             throw new NullPointerException();          } catch(Throwable t) {             System.out.println("Caught "+t);             throw t;         }     } } 

It also compiles if Throwable is changed to Exception.

This does not compile, as expected:

public class Test {     public static void main(String[] args) {         try {             throw new NullPointerException();          } catch(Throwable t) {             Throwable t2 = t;             System.out.println("Caught "+t2);             throw t2;         }     } } 

This compiles:

public class Test {     public static void main(String[] args) {         try {             throwsRuntimeException();          } catch(Throwable t) {             System.out.println("Caught "+t);             throw t;         }     }      public static void throwsRuntimeException() {         throw new NullPointerException();     } } 

This does not:

public class Test {     public static void main(String[] args) {         try {             throwsCheckedException();          } catch(Throwable t) {             System.out.println("Caught "+t);             throw t;         }     }      public static void throwsCheckedException() {         throw new java.io.IOException();     } } 

This compiles as well:

public class Test {     public static void main(String[] args) throws java.io.IOException {         try {             throwsIOException();          } catch(Throwable t) {             System.out.println("Caught "+t);             throw t;         }     }      public static void throwsIOException() throws java.io.IOException {         throw new java.io.IOException();     } } 

A more complex example - the checked exception is caught by an outer catch block, instead of being declared to be thrown. This compiles:

public class Test {     public static void main(String[] args) {         try {             try {                 throwsIOException();              } catch(Throwable t) {                 System.out.println("Caught "+t);                 throw t;             }         } catch(java.io.IOException e) {             System.out.println("Caught IOException (outer block)");         }     }      public static void throwsIOException() throws java.io.IOException {         throw new java.io.IOException();     } } 

So there seems to be a special case to allow rethrowing exceptions when the compiler can determine that the caught exception is always legal to re-throw. Is this correct? Where is this specified in the JLS? Are there any other obscure corner-cases like this?

like image 292
user253751 Avatar asked May 10 '14 13:05

user253751


People also ask

Why throwable should not be caught?

Don't Catch Throwable Throwable is the superclass of all exceptions and errors. You can use it in a catch clause, but you should never do it! If you use Throwable in a catch clause, it will not only catch all exceptions; it will also catch all errors.

Is throwable an exception?

Throwable is super class of Exception as well as Error . In normal cases we should always catch sub-classes of Exception , so that the root cause doesn't get lost. Only special cases where you see possibility of things going wrong which is not in control of your Java code, you should catch Error or Throwable .

Is it possible to throw exception without using throws exception in Java?

Without using throws When an exception is cached in a catch block, you can re-throw it using the throw keyword (which is used to throw the exception objects). If you re-throw the exception, just like in the case of throws clause this exception now, will be generated at in the method that calls the current one.

Can throwable be thrown?

The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement.


2 Answers

This is covered by JLS 11.2.2 (emphasis mine):

A throw statement whose thrown expression is a final or effectively final exception parameter of a catch clause C can throw an exception class E iff:

  • E is an exception class that the try block of the try statement which declares C can throw; and

  • E is assignment compatible with any of C's catchable exception classes; and

(...)

In other words, E, the type referenced in the doc, is the type that can be thrown, not the type of the catch clause parameter that catches it (the catchable exception class). It just has to be assignment compatible to the catch clause parameter, but the parameter's type is not used in analysis.

This is why the go out of their way to say a final or effectively final exception parameter--if t in your example were reassigned, the analysis would go out the window.

like image 123
Mark Peters Avatar answered Sep 27 '22 21:09

Mark Peters


Because the compiler is smart enough to know that a checked exception can not be thrown from the try block, and the caught Throwable is thus not a checked exception that must be declared.

Note that this is true since Java 7, if I'm not mistaken.

like image 25
JB Nizet Avatar answered Sep 27 '22 21:09

JB Nizet