Reputation: 147
I am having trouble understanding how precise rethrow works in Java 7 and later versions. As pointed out in https://www.theserverside.com/tutorial/OCPJP-Use-more-precise-rethrow-in-exceptions-Objective-Java-7, in Java 7 and later versions we can use the throws
clause, in a method declaration, with a comma-separated list of specific exceptions that the method could throw. If all these exceptions are subtypes of the general exception java.lang.Exception
, we will be able to catch any of them in a catch block that catches this supertype, while letting client code (eg. a caller method) to know which of the possible subtypes exceptions actually occurred.
Initially, I thought that in order to let know client code which exception actually occurred, we needed to specify the list of specific exceptions in the throws
clause. Nevertheless, in the following example the client code (the main()
method) seems able to retrieve that information, even if we only specify the exception java.lang.Exception
in the throws
clause of the called method. Therefore, my question is:
Why the following code outputs the same, regardless of whether the throws
clause of the method runException()
is throws ExceptionA, ExceptionB
or throws Exception
?
I am using Oracle JVM-12 in Eclipse. Thanks in advance!
class ExceptionA extends Exception{}
class ExceptionB extends Exception{}
public class RethrowingAndTypeChecking{
public static void runException(char what) throws Exception{
//public static void runException(char what) throws ExceptionA, ExceptionB{
try{
if(what == 'A')
throw new ExceptionA();
else if (what == 'B')
throw new ExceptionB();
}
catch(Exception e){
throw e;
}
}
public static void main (String args[]){
char ch;
for (int i=0;i<2;i++) {
if(i==0) ch='A';
else ch = 'B';
try{
runException(ch);
}
catch(ExceptionA e){
System.out.print("In main(), 'catch(ExceptionA e){}', caught exception: " + e.getClass());
}
catch(ExceptionB e){
System.out.print("In main(), 'catch(ExceptionB e){}', caught exception: " + e.getClass());
}
catch(Exception e){
System.out.print("In main(), 'catch(Exception e){}', caught exception: " + e.getClass());
}
System.out.println();
}
}
}
output:
In main(), 'catch(ExceptionA e){}', caught exception: class ExceptionA
In main(), 'catch(ExceptionB e){}', caught exception: class ExceptionB
Upvotes: 2
Views: 467
Reputation: 21
It's because, you've been throwing the Subclasses at,
try{
if(what == 'A')
throw new ExceptionA();
else if (what == 'B')
throw new ExceptionB();
}
of "Exception class" which are in turn being thrown out at,
catch(Exception e){
throw e;
}
after being assigned to "Exception class( at Exception e)", it will not make a difference if you specify throwing a Superclass type throws objectReference at
public static void runException(char what) throws Exception){
or Subclass type throws objectReferences at
public static void runException(char what) throws ExceptionA, ExceptionB){
Upvotes: 2
Reputation: 147
Quoting @Carlos Heuberger, my code outputs the same, regardless of whether the throws
clause of the method runException()
is throws ExceptionA, ExceptionB
or throws Exception
because:
the run-time type of the exception is used to select the catch clause: see 14.20.1. Execution of try - catch
Whatever the exception reference type (in this case ExceptionA
, ExceptionB
or Exception
) used to refer to the exception object thrown by method runException()
, such method will throw objects of type either ExceptionA
or ExceptionB
. These objects will be assignment compatible with the catch parameters of the first two catch of the main()
method.
After paragraphs 8.4.6, 11.2.3 and 14.20.1 of the Java Language Specification, I understood that what we actually specify in a throws
clause of a method signature is the list of the exception reference types that will be assignment compatible with any possible exception object thrown from the method (given a class reference type we can make it point to instance objects of itself or to instance objects of its subclasses, not superclasses ). That tells any other caller method what exceptions it may have to deal with when invoking the method with the throws clause. In my code example, the advantage of using the clause throws ExceptionA, ExceptionB
is that I will not need to catch java.lang.Exception in the main()
. In fact, if I choose clause throws Exception
in method runException()
and delete the cath(Exception)
block from the main()
I will get a compile-time error. This is because even if we will be throwing ExceptionA
or ExceptionB
objects at run-time, the compiler will understand that method runException()
may throw out an exception object of type Exception
, which will not be assignment compatible with any of the catch parameters in the main()
(Exception
is a superclass of both ExceptionA
and ExceptionB
).
Upvotes: 1
Reputation: 45339
What you're missing is the case where you need to handle those possible exceptions in different ways. Your code is catching individual exceptions, but it is, roughly speaking, performing the same action.
If you were to handle ExceptionA
in a considerably different way from how you handle ExceptionB
, then catching the broad Exception
would not allow you to do that specifically:
catch(Exception e){
// something unexpected happened
// e could be an ExceptionA problem
// e could be an ExceptionB problem
// e could be any other unchecked exception
}
When the catch(Exception e){}
block is entered, the exception could pretty much be anything, but you have only one generic code block to handle it.
Beside this, if the method you're calling declares specific checked exceptions, then the compiler can help you handle only those exceptions, thus adding to the predictability of the code
try{
runException(ch);
} catch(ExceptionA e){
// code specific to handling ExceptionA problems
} catch(ExceptionB e){
// code specific to handling ExceptionB problems
} catch(ExceptionC e){ //will not compile, because not declared by runException
// code specific to handling ExceptionB problems
}
Upvotes: 1
Reputation: 35096
As a rule, you should never catch (Exception ex)
. Because this will catch RuntimeExceptions too. It sometimes makes sense to catch (Throwable t) or to use Thread.setDefaultUncaughtExceptionHandler to customize your uncaught exception handler to catch exceptions and then display them to the user. Sometimes I will catch an Exception, wrap it in a RuntimeException (or an Error) and throw that
When it comes to exceptions, you should really only be catching them when you can do something with them, or when you want to make sure that an exception doesn't cause the rest of the method to not process.
Personally I divide exceptions into 3 types
Upvotes: 1
Reputation: 46963
These throws declarations are so that you list more explicitly what happens out of the method. Otherwise this is ordinary polymorphism: you use base class to combine in multiple subclasses, however you are definitely not changing the instances, this is why at runtime in both cases the exceptions are resolved to their concrete classes.
Upvotes: 1