I'm rather puzzled by your question. There's clearly something about java exceptions / exception handling that you don't understand. So lets start at the beginning.
In java, all exceptions (in the sense that this term is used in the Java Language Specification) are instances of a some class that is a subclass of java.lang.Throwable
. There are two (and only two) direct subclasses of Throwable; i.e. java.lang.Exception
and java.lang.Error
. Instances of all of these classes ... including instances of Throwable and Error ... are referred to as exceptions in the JLS.
An exception handler catches exceptions (in the JLS sense) that are assignment compatible with the exception type used in the catch
declaration. So for example:
try {
....
} catch (Exception ex) {
...
}
will catch any exception thrown in the try
block that is an instance of java.lang.Exception
or of a direct or indirect subtype of java.lang.Exception
. But it won't catch an instance of java.lang.Throwable
, because that is (obviously) not one of the above.
On the other hand:
try {
....
} catch (Throwable ex) {
...
}
will catch an instance of java.lang.Throwable
.
Reviewing your example in the light of this, it is obvious why the f
method is not catching the Throwable instance: it doesn't match the exception type in the catch clause! By contrast, in the g
method the Exception instance matched the exception type in the catch clause and is therefore caught.
I don't understand what you are saying about needing to throw a Throwable in g
. Firstly, the fact that the method declares that it throws Throwable does not mean that it actually needs to throw it. All it is saying is that it might throw something assignable to Throwable ... possibly in some future version of the g
method. Secondly, if you were to add throw e;
to the outer catch block, it would be throwing something that is assignable to Throwable.
Finally, it is generally a bad idea to be creating instances of Throwable, Exception, Error and RuntimeException. And you need to be very careful when and how you catch them. For example:
try {
// throws an IOException if file is missing
InputStream is = new FileInputStream("someFile.txt");
// do other stuff
} catch (Exception ex) {
System.err.println("File not found");
// WRONG!!! We might have caught some completely unrelated exception;
// e.g. a NullPointerException, StackOverflowError,
}
EDIT - in response OP's comments:
But what I throw with throw e.fillInStackTrace(); should be an Intance of Throwable,not Exception!
The Javadoc says specifically that the object returned is the exception object you are calling the method on. The purpose of the fillInStacktrace()
method is to fill in the stack trace for an existing object. If you want a different exception, you should use new
to create one.
Actually,I mean the outter exception handler should not catch the Throwable thrown by throw e.fillInStackTrace().
I have explained why it does - because the Throwable is actually the original Exception. Is there something about my explanation that you do not understand or are you simply saying that you don't like the way that Java is defined?
EDIT 2
And if the outter exception handler could handle the Throwable exception,why must we specify that the method g would throw Throwable exception
You misunderstand what I was saying ... which was that if you DID throw an Exception, then the throws Throwable
would not be redundant. OTOH, I finally think I understand your complaint.
I think that the crux of your complaint is that you'd get a compilation error with this:
public void function() throws Exception {
try {
throw new Exception();
} catch (Exception ex) {
throw ex.fillInStackTrace();
// according to the static type checker, the above throws a Throwable
// which has to be caught, or declared as thrown. But we "know" that the
// exception cannot be anything other than an Exception.
}
}
I can see that this is somewhat unexpected. But it is unavoidable I'm afraid. There is NO way (short of a major change to Java's type system) that you could declare the signature of the fillInStacktrace
that will work in all cases. For example, if you moved the declaration of the method to the Exception class, you'd just repeat the same problem with subtypes of Exception. But if you tried to express the signature using a generic type parameter, it would entail making all subclasses of Throwable explicit generic types.
Fortunately, the cure is really simple; cast the result of fillInStacktrace()
as follows:
public void function() throws Exception {
try {
throw new Exception();
} catch (Exception ex) {
throw (Exception) (ex.fillInStackTrace());
}
}
And the final point is that it is very unusual for an application to explicitly call fillInStacktrace()
. In the light of that, it would simply have not been worthwhile for the Java designers to have "busted their guts" trying to solve this. Especially since it is really only a minor inconvenience ... at most.