The following code compiles and runs on Java 13:
public class CheckedExceptionSSCE {
public static void main(String[] args) {
try {
methodNoThrowsDeclaration();
} catch (Exception e) {
// why is this throw allowed?
// no throws in main()
throw e;
}
}
private static void methodNoThrowsDeclaration() {
System.out.println("doesn't throw");
}
}
How come the throw e is allowed?
Is it specified somewhere in the JLS? I was not able to find it, perhaps I'm using wrong keywords to search.
Is the compiler smart enough to deduce that there will be no real checked exception thrown and thus allows the code to compile and run?
This is a feature that was added in Java 7.
The compiler can derive the type of exception if you use a variable from the catch clause to rethrow the exception. Since you have no checked exception to be caught, it knows that e could only be RuntimeException and no throws definition is needed.
More information:
https://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html
The method:
private static void methodNoThrowsDeclaration() {
System.out.println("doesn't throw");
}
does not throw any checked exception, from this SO thread one can read:
You can throw unchecked exceptions without having to declare them if
you really want to. Unchecked exceptions extend RuntimeException.
therefore there is no need to adapt the main method signature.
From the Java language specification (§14.18 The throw Statement) one can read:
ThrowStatement: throw Expression ;
At least one of the following three conditions must be true, or a
compile-time error occurs:
The type of the Expression is an unchecked exception class (§11.1.1)
or the null type (§4.1).
The throw statement is contained in the try block of a try statement
(§14.20) and it is not the case that the try statement can throw an
exception of the type of the Expression. (In this case we say the
thrown value is caught by the try statement.)
The throw statement is contained in a method or constructor
declaration and the type of the Expression is assignable (§5.2) to at
least one type listed in the throws clause (§8.4.6, §8.8.5) of the
declaration.
The code that you have shown follows at least the first condition. However, let us look at the following example, where non of those three aforementioned conditions are true, namely if you do :
private static void methodNoThrowsDeclaration() {
System.out.println("doesn't throw");
throw new Exception();
}
that would force you to do:
private static void methodNoThrowsDeclaration() throws Exception {
System.out.println("doesn't throw");
throw new Exception();
}
which in turn would give a compiler error:
Unhandled exception: java.lang.Exception
at the statement throw e;.
Related
Consider the following example:
public class LambdaArgsTest {
private static void display(Supplier<?> arg) {
try {
// this is the place where the Exception("wrong") might be thrown
// and it is in fact handled
System.out.println(arg.get());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
display(() -> {
if(/*some condition*/) {
// this statement will be rejected due to unhandled exception
throw new Exception("wrong");
}
return "abcde";
});
}
}
Here comes the question: the lambda argument in the above example is an object that'll be executed later inside the "display()" method. It is obviously not executed when passing the arguments to the "display()".
Why is it rejected by the compiler? I think it's quite reasonable to surround it by try...catch only when the lambda is actually called.
This is because of the signature of Supplier functional interface:
T get();
As you see, the method get is not declared to throw Exception (neither any other checked exception).
In Java, there are checked vs unchecked exceptions (unchecked exceptions are those which inherit from RuntimeException). Checked exceptions must be handled, either by catching them in a catch block, or by declaring that the method throws that exception.
If the signature of Supplier.get were:
T get() throws Exception:
The code would compile fine.
Try throwing RuntimeException instead of Exception and the code will compile fine.
EDIT: As per Peter Lawrey's suggestion in the comments, if you do need to throw a checked exception from within your lambda expression, you could use e.g. Callable, whose only one method's signature is as follows:
T call() throws Exception;
You'd just need to pass a Callable to your display method instead of a Supplier.
I have a hard time to understand why the following code compiles, while it is not a subclass of exception:
class Test
{
public void run() throws IOException
{
System.out.println("Test");
}
}
class SubTest extends Test
{
//not a subclass of IOException, still compiles
public void run() throws RuntimeException
{
System.out.println("Test from sub");
}
}
class Sub2Test extends Test
{
//not a subclass of IOException, does not compile
public void run() throws Exception
{
System.out.println("Test from sub");
}
}
I understand RuntimeException is an unchecked exception, but I thought the rule was that it must be a subclass of the parent exception?
Imagine there is a caller which calls Test#run. In the declaration of Test#run, it says it might throw IOException, so the caller knows it can catch and handle it:
Test test = // it could be instance of SubTest of Sub2Test
try {
test.run();
} catch (IOException e) {
}
Then it's ok if SubTest does not throw IOException, the caller will not miss anything.
But if you throw some checked Exception like Sub2Test, since the caller does not know it until runtime, the called is not able to catch and handle it. So it should not be compiled.
"I understand RuntimeException is an unchecked exception, but I thought the rule was that it must be a subclass of the parent exception?"
That is the general rule, as specified in the JLS in section §11.2. Compile-Time Checking of Exceptions, which states (emphasis mine)
The throws clause of an overriding method may not specify that this method will result in throwing any checked exception which the overridden method is not permitted, by its throws clause, to throw (§8.4.8.3).
But that only applies to checked exceptions, and it also explicitly states that
The unchecked exception classes (§11.1.1) are exempted from compile-time checking.
So the compiler is going to ignore the fact that RuntimeException isn't a subclass of IOException.
I have another challenging question from javaDeathMatch game;
In the code below we are asked what kind of problem the code below has.
please correct me if I am wrong;
compilation error : None; At compile time the erasure of type parameter has not still occurred and the dynamic binding has not been taken place, so the parameter passed to the method which is of type SQLException is thought of as Exception in the method 'pleaseThrow' and it(i mean Exception not SQLException) is cast to Runtime Exception in the method with no error. The only error is that we don't have a proper catch clause to catch the exception.
public class Exption<T extends Exception> {
public static void main(String[] args) {
try {
new Exption<RuntimeException>().pleaseThrow(new SQLException());
}catch (final SQLException ex){
ex.printStackTrace();
}
}
private void pleaseThrow(final Exception t) throws T{
throw (T)t;
}
}
if we replace the catch clause with this:
catch(final RuntimeException e){
e.printStackTrace();
System.err.println("caught");
}
the exception will be caught but the System.err.println("caught") will never be printed!!! What is the problem????
This is due to type erasure. In java after compilation, every generic information is lost (there is something left, which is not relevant to this though). That means that during compilation, the generic variable T is equal to RuntimeException. So your pleaseThrow code looks like this:
private void pleaseThrow(final Exception t) throws RuntimeException{
throw (RuntimeException)t;
}
After compilation though, every generic parameter is erased to the base type. In your case, to Exception. Which leaves you with a method signature like this:
private void pleaseThrow(final Exception t) throws Exception{
throw (Exception)t;
}
Which finally is clear, why your catch block is never reached. You're trying to catch RuntimeExceptions but what you're actually throwing is a checked exception. Which then propagates upwards and is finally caught by the JVM.
Additional reading: Oracle Tutorial on type erasure
This code will fail to compile, because SQLException is a checked exception, and to catch a checked exception, it must be declared to be thrown by something inside of the try block. Here it is failing to compile on Ideone, for example, with the following message:
Main.java:7: error: exception SQLException is never thrown in body of corresponding try statement
}catch (final SQLException ex){
^
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
If you change the catch block so it catches RuntimeException, then the code will compile, but the exception will not be caught, because SQLException is not a subclass of RuntimeException.
There's a discussion of how the pleaseThrow method works here. That idiom is usually called "sneaky throws", and it lets the caller throw a checked exception as if it was an unchecked exception. For the difference between checked and unchecked exceptions, see the official tutorial or this Q&A and StackOverflow.
Let us take the first version of your code :
public class Exption<T extends Exception> {
public static void main(String[] args) {
try {
new Exption<RuntimeException>().pleaseThrow(new SQLException());
}catch (final SQLException ex){ // This is compilation error
ex.printStackTrace();
}
}
private void pleaseThrow(final Exception t) throws T{
throw (T)t;
}
}
How is compiler able to detect the error ?
It is not because of throw (T)t;. Infact , if you type cast to a type parameter , compiler ignores it and leaves it to JVM.
Then how does compiler able to generate error ?
It is because of this :
private void pleaseThrow(final Exception t) throws T
Notice the throws T . When you say new Exption<RuntimeException>() , compiler does not create any object. But it infers T during compilation. It comes under it powers.
Now , let us understand the whole picture of why error gets generated.
Compiler from private void pleaseThrow(final Exception t) throws T{ knows that you will throw RuntimeException.
According to rule of type casting , compiler checks both types if one is parent of other. If yes , it passes the code to JVM . Only JVM will then further check if one object can actually be type casted to other.
Similarly, compiler checks for throws and catch block if any is compatible or not. If more than one is compatible, it leaves the decision to JVM. Compiler checks for many other things too but let us focus on the main track.
In your example, One type is SqlException and other is RuntimeException. None is parent of other. Hence, compiler shows error.
Let us see few examples to clear it :
public class Exption<T extends Exception> {
public static void main(String[] args) {
try {
new Exption<RuntimeException>().pleaseThrow(new IllegalArgumentException());
}catch (final ClassCastException ex){
ex.printStackTrace();
System.out.println("done");
}
}
private void pleaseThrow(final Exception t) throws T{
throw (T)t;
}
}
In this example, Catch clause won't be called , however code compiles fine.
Since ClassCastException is a RuntimeException , Compiler compiles the code fine. RuntimeException is parent to ClassCastException.
But when compiler hands over code to JVM, JVM knows that exception object is of type IllegalArgumentException and hence will settle for a catch clause of IllegalArgumentException or it's super type. Here we have nothing like that. Hence, Catch clause won't be called as there is no match.
Take another example :
public class Exption<T extends Exception> {
public static void main(String[] args) {
try {
new Exption<RuntimeException>().pleaseThrow(new IllegalArgumentException());
}catch (final RuntimeException ex){
ex.printStackTrace();
System.out.println("done");
}
}
private void pleaseThrow(final Exception t) throws T{
throw (T)t;
}
}
This runs fine and catch block is called. JVM knows object type will be IllegalArgumentException and RuntimeException is super class , hence , it matches due to super class able to refer child class object.
Now, let us come back to your code.
You have only written only one catch block consisting of SqlException which is a checked exception and hence cannot be thrown from pleaseThrow() as it throws RuntimeException according to compiler.
So, this error gets generated :
Error:(9, 10) java: exception java.sql.SQLException is never thrown in body of corresponding try statement
As you know it is illegal in Java to have a catch block catching a checked expression never thrown.
Now , let us come to version 2 of your code :
public class Exption<T extends Exception> {
public static void main(String[] args) {
try {
new Exption<RuntimeException>().pleaseThrow(new SQLException());
}catch(final RuntimeException e){
e.printStackTrace();
System.err.println("caught");
}
}
private void pleaseThrow(final Exception t) throws T{
throw (T)t;
}
}
Now, things make sense as in catch block you have written RuntimeException.
Compiler sees that method throws RuntimeException and in catch block too we have runtime exception.
Now, let us look more closely. Compiler easily compiles this code and send it to JVM. But before that it does the type erasure.
Let us look what would type erasure would have done to our code :
[In actual code given to JVM is byte level code. The below example is to show what type erasure does to the code. Note that below code won't run properly in compiler if you try to compile as this code is after type erasure and few details have been lost which compiler needs. JVM however can run the equivalent byte code of this.]
public class Exption {
public static void main(String[] args) {
try {
new Exption().pleaseThrow(new SQLException());
}catch(final RuntimeException e){
e.printStackTrace();
System.err.println("caught");
}
}
private void pleaseThrow(final Exception t) throws java.lang.Exception {
throw (java.lang.Exception) t;
}
}
To understand how this code was reduced to , you need to read about type erasure. But trust me till here for time being.
Now, this code is very interesting piece of code.
On this code JVM directly operates. If you see JVM knows it method throws an Object of type SqlException type casted to Exception. It tries to find a match in catch blocks but no match. RunTimeException is not a super class of SqlException. Hence, no catch block is called.
Let us modify the code to understand it more.
public class Exption<T extends Exception> {
public static void main(String[] args) {
try {
new Exption<RuntimeException>().pleaseThrow(new SQLException());
}catch(final RuntimeException e){
e.printStackTrace();
System.err.println("caught");
}catch(Exception r){
System.out.println("done");
}
}
private void pleaseThrow(final Exception t) throws T{
throw (T)t;
}
}
This will output "done".
In Java, methods that throw checked exceptions (Exception or its subtypes - IOException, InterruptedException, etc) must declare throws statement:
public abstract int read() throws IOException;
Methods that do not declare throws statement can't throw checked exceptions.
public int read() { // does not compile
throw new IOException();
}
// Error: unreported exception java.io.IOException; must be caught or declared to be thrown
But catching checked exceptions in safe methods is still legal in java:
public void safeMethod() { System.out.println("I'm safe"); }
public void test() { // method guarantees not to throw checked exceptions
try {
safeMethod();
} catch (Exception e) { // catching checked exception java.lang.Exception
throw e; // so I can throw... a checked Exception?
}
}
Actually, no. It's a bit funny: compiler knows that e is not a checked exception and allows to rethrow it. Things are even a bit ridiculous, this code does not compile:
public void test() { // guarantees not to throw checked exceptions
try {
safeMethod();
} catch (Exception e) {
throw (Exception) e; // seriously?
}
}
// Error: unreported exception java.lang.Exception; must be caught or declared to be thrown
The first snippet was a motivation for a question.
Compiler knows that checked exceptions can't be thrown inside a safe method - so maybe it should allow to catch only unchecked exceptions?
Returning to the main question - are there any reasons to implement catching checked exceptions in this way? Is it just a flaw in the design or am I missing some important factors - maybe backward incompatibilities? What could potentially go wrong if only RuntimeException were allowed to be catched in this scenario? Examples are greatly appreciated.
Quoting the Java Language Specification, §11.2.3:
It is a compile-time error if a catch clause can catch checked exception class E1 and it is not the case that the try block corresponding to the catch clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception or a superclass of Exception.
I'm guessing that this rule originated long before Java 7, where multi-catches did not exist. Therefore, if you had a try block that could throw a multitude of exceptions, the easiest way to catch everything would be to catch a common superclass (in the worst case, Exception, or Throwable if you want to catch Errors as well).
Note that you may not catch an exception type that is completely unrelated to what is actually thrown - in your example, catching any subclass of Throwable that is not a RuntimeException will be an error:
try {
System.out.println("hello");
} catch (IOException e) { // compilation error
e.printStackTrace();
}
Edit by OP: The main part of the answer is the fact that question examples work only for Exception class. Generally catching checked exceptions is not allowed in random places of the code. Sorry if I confused somebody using these examples.
Java 7 introduced more inclusive exception type checking.
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.
This passage is talking about a try block that specifically throws FirstException and SecondException; even though the catch block throws Exception, the method only needs to declare that it throws FirstException and SecondException, not Exception:
public void rethrowException(String exceptionName)
throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}
This means that the compiler can detect that the only possible exception types thrown in test are Errors or RuntimeExceptions, neither of which need to be caught. When you throw e;, it can tell, even when the static type is Exception, that it doesn't need to be declared or re-caught.
But when you cast it to Exception, this bypasses that logic. Now the compiler treats it as an ordinary Exception which needs to be caught or declared.
The main reason for adding this logic to the compiler was to allow the programmer to specify only specific subtypes in the throws clause when rethrowing a general Exception catching those specific subtypes. However, in this case, it allows you to catch a general Exception and not have to declare any exception in a throws clause, because no specific types that can be thrown are checked exceptions.
The issue here is that checked/unchecked exception limitations affect what your code is allowed to throw, not what it's allowed to catch. While you can still catch any type of Exception, the only ones you're allowed to actually throw again are unchecked ones. (This is why casting your unchecked exception into a checked exception breaks your code.)
Catching an unchecked exception with Exception is valid, because unchecked exceptions (a.k.a. RuntimeExceptions) are a subclass of Exception, and it follows standard polymorphism rules; it doesn't turn the caught exception into an Exception, just as storing a String in an Object doesn't turn the String into an Object. Polymorphism means that a variable that can hold an Object can hold anything derived from Object (such as a String). Likewise, as Exception is the superclass of all exception types, a variable of type Exception can hold any class derived from Exception, without turning the object into an Exception. Consider this:
import java.lang.*;
// ...
public String iReturnAString() { return "Consider this!"; }
// ...
Object o = iReturnAString();
Despite the variable's type being Object, o still stores a String, does it not? Likewise, in your code:
try {
safeMethod();
} catch (Exception e) { // catching checked exception
throw e; // so I can throw... a checked Exception?
}
What this means is actually "catch anything compatible with class Exception (i.e. Exception and anything derived from it)." Similar logic is used in other languages, as well; for example, in C++, catching a std::exception will also catch std::runtime_error, std::logic_error, std::bad_alloc, any properly-defined user-created exceptions, and so on, because they all derive from std::exception.
tl;dr: You're not catching checked exceptions, you're catching any exceptions. The exception only becomes a checked exception if you cast it into a checked exception type.
In Java, methods that throw checked exceptions (Exception or its subtypes - IOException, InterruptedException, etc) must declare throws statement:
public abstract int read() throws IOException;
Methods that do not declare throws statement can't throw checked exceptions.
public int read() { // does not compile
throw new IOException();
}
// Error: unreported exception java.io.IOException; must be caught or declared to be thrown
But catching checked exceptions in safe methods is still legal in java:
public void safeMethod() { System.out.println("I'm safe"); }
public void test() { // method guarantees not to throw checked exceptions
try {
safeMethod();
} catch (Exception e) { // catching checked exception java.lang.Exception
throw e; // so I can throw... a checked Exception?
}
}
Actually, no. It's a bit funny: compiler knows that e is not a checked exception and allows to rethrow it. Things are even a bit ridiculous, this code does not compile:
public void test() { // guarantees not to throw checked exceptions
try {
safeMethod();
} catch (Exception e) {
throw (Exception) e; // seriously?
}
}
// Error: unreported exception java.lang.Exception; must be caught or declared to be thrown
The first snippet was a motivation for a question.
Compiler knows that checked exceptions can't be thrown inside a safe method - so maybe it should allow to catch only unchecked exceptions?
Returning to the main question - are there any reasons to implement catching checked exceptions in this way? Is it just a flaw in the design or am I missing some important factors - maybe backward incompatibilities? What could potentially go wrong if only RuntimeException were allowed to be catched in this scenario? Examples are greatly appreciated.
Quoting the Java Language Specification, §11.2.3:
It is a compile-time error if a catch clause can catch checked exception class E1 and it is not the case that the try block corresponding to the catch clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception or a superclass of Exception.
I'm guessing that this rule originated long before Java 7, where multi-catches did not exist. Therefore, if you had a try block that could throw a multitude of exceptions, the easiest way to catch everything would be to catch a common superclass (in the worst case, Exception, or Throwable if you want to catch Errors as well).
Note that you may not catch an exception type that is completely unrelated to what is actually thrown - in your example, catching any subclass of Throwable that is not a RuntimeException will be an error:
try {
System.out.println("hello");
} catch (IOException e) { // compilation error
e.printStackTrace();
}
Edit by OP: The main part of the answer is the fact that question examples work only for Exception class. Generally catching checked exceptions is not allowed in random places of the code. Sorry if I confused somebody using these examples.
Java 7 introduced more inclusive exception type checking.
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.
This passage is talking about a try block that specifically throws FirstException and SecondException; even though the catch block throws Exception, the method only needs to declare that it throws FirstException and SecondException, not Exception:
public void rethrowException(String exceptionName)
throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}
This means that the compiler can detect that the only possible exception types thrown in test are Errors or RuntimeExceptions, neither of which need to be caught. When you throw e;, it can tell, even when the static type is Exception, that it doesn't need to be declared or re-caught.
But when you cast it to Exception, this bypasses that logic. Now the compiler treats it as an ordinary Exception which needs to be caught or declared.
The main reason for adding this logic to the compiler was to allow the programmer to specify only specific subtypes in the throws clause when rethrowing a general Exception catching those specific subtypes. However, in this case, it allows you to catch a general Exception and not have to declare any exception in a throws clause, because no specific types that can be thrown are checked exceptions.
The issue here is that checked/unchecked exception limitations affect what your code is allowed to throw, not what it's allowed to catch. While you can still catch any type of Exception, the only ones you're allowed to actually throw again are unchecked ones. (This is why casting your unchecked exception into a checked exception breaks your code.)
Catching an unchecked exception with Exception is valid, because unchecked exceptions (a.k.a. RuntimeExceptions) are a subclass of Exception, and it follows standard polymorphism rules; it doesn't turn the caught exception into an Exception, just as storing a String in an Object doesn't turn the String into an Object. Polymorphism means that a variable that can hold an Object can hold anything derived from Object (such as a String). Likewise, as Exception is the superclass of all exception types, a variable of type Exception can hold any class derived from Exception, without turning the object into an Exception. Consider this:
import java.lang.*;
// ...
public String iReturnAString() { return "Consider this!"; }
// ...
Object o = iReturnAString();
Despite the variable's type being Object, o still stores a String, does it not? Likewise, in your code:
try {
safeMethod();
} catch (Exception e) { // catching checked exception
throw e; // so I can throw... a checked Exception?
}
What this means is actually "catch anything compatible with class Exception (i.e. Exception and anything derived from it)." Similar logic is used in other languages, as well; for example, in C++, catching a std::exception will also catch std::runtime_error, std::logic_error, std::bad_alloc, any properly-defined user-created exceptions, and so on, because they all derive from std::exception.
tl;dr: You're not catching checked exceptions, you're catching any exceptions. The exception only becomes a checked exception if you cast it into a checked exception type.