Rethrowing an Exception: Why does the method compile without a throws clause?
On the source code below I'm rethrowing an Exception
.
Why is it not necessary to put the throws
keyword on the method's signature?
public void throwsOrNotThrowsThatsTheQuestion() {
try {
// Any processing
} catch (Exception e) {
throw e;
}
}
This behavior appears to occur only on Java 1.7. When compiling with 1.6, I get the following compiler error message:
c:\dev\src\misc>javac -source 1.6 Main.java
warning: [options] bootstrap class path not set in conjunction with -source 1.6
Main.java:22: error: unreported exception Exception; must be caught or declared
to be thrown
throw e;
^
1 error
1 warning
But with Java 1.7, it compiles.
c:\dev\src\misc>javac -source 1.7 Main.java
c:\dev\src\misc>
... Until I actually throw an Exception
in the try
block:
public static void throwsOrNotThrowsThatsTheQuestion() {
try {
// Any processing
throw new IOException("Fake!");
} catch (Exception e) {
throw e;
}
Compiling...
c:\dev\src\misc>javac -source 1.7 Main.java
Main.java:22: error: unreported exception IOException; must be caught or declare
d to be thrown
throw e;
^
1 error
It looks like Java 1.7 got smart enough to detect the kind of Exception
(s) that might be thrown by analyzing the try
block code, where as 1.6 just saw throw e;
of type Exception
and gave an error just for that.
Changing it to throw a RuntimeException
made it compile as expected, because as always, unchecked Exception
s don't need a throws
clause:
public static void throwsOrNotThrowsThatsTheQuestion() {
try {
// Any processing
throw new RuntimeException("Fake!");
} catch (Exception e) {
throw e;
}
Compiling...
c:\dev\src\misc>javac -source 1.7 Main.java
c:\dev\src\misc>
The Explanation
Here's what's going on:
Java 7 introduced more inclusive type checking. Quoting...
Consider the following example:
static class FirstException extends Exception { }
static class SecondException extends Exception { }
public void rethrowException(String exceptionName) throws Exception {
try {
if (exceptionName.equals("First")) {
throw new FirstException();
} else {
throw new SecondException();
}
} catch (Exception e) {
throw e;
}
}
This examples's try block could throw either FirstException or SecondException. Suppose you want to specify these exception types in the throws clause of the rethrowException method declaration. In releases prior to Java SE 7, you cannot do so. Because the exception parameter of the catch clause, e, is type Exception, and the catch block rethrows the exception parameter e, you can only specify the exception type Exception in the throws clause of the rethrowException method declaration.
However, in Java SE 7, you can specify the exception types FirstException and SecondException in the throws clause in the rethrowException method declaration. The Java SE 7 compiler can determine that the exception thrown by the statement throw e must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException. Even though the exception parameter of the catch clause, e, is type Exception, the compiler can determine that it is an instance of either FirstException or SecondException:
(emphasis mine)
public void rethrowException(String exceptionName)
throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}
java.lang.Exception is a checked exception so this won't work or even compile. It would work with a unckeched (java.lang.RuntimeException). It makes absolutly no difference whether you throw an exception inside a catch block or not.
The compiler error would look something like this (depending on the compiler):
java: unreported exception java.lang.Exception; must be caught or declared to be thrown
EDIT: Java 7 can deal with such situations if you never actually throw the exception
If you thorw an checked exception you need to have it in the throws list
public void retrhowChecked() throws Exception {
try {
throw new IOException();
} catch(Exception e) {
throw e;
}
}
If you throw an unchecked exception you don't need to put it in the throws list, you can use this to pack a checked Exception inside an unchecked one to avoid breaking code that uses this method if you change the method in question in such a way that it after the change may produce a checked exception. But you have to be careful with that, checked Exception are there to be handled!
public void retrhowUnchecked() {
try {
throw new IOException();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
Read more about Exceptions here.
Why is not necessary to put the throws keyword on the method's signature?
You can put that cause your // Any processing
is not throwing any checked-exception.
Example:
This compile fine.
public void throwsOrNotThrowsThatsTheQuestion() {
try {
throw new RuntimeException();
} catch (Exception e) {
throw e;
}
This won't compile, you need to add throws
clause.
public void throwsOrNotThrowsThatsTheQuestion() {
try {
throw new Exception();
} catch (Exception e) {
//do something like log and rethrow
throw e;
}
}
This is working since java 7. In previous version an exception is thrown. More information rethrow in java 7