Solution to 2862: Exceptions and inheritance (ii)
See code at solutions/code/tutorialquestions/question2862
The reason that the classes compile even though foo()
in B
throws a different exception
from foo()
in A
is that the exception, MyExceptionD
, thrown by foo
in B
is a subclass of MyExceptionC
thrown by foo
in A
.
It is OK when overriding a method to narrow the types of exceptions that can be thrown. As discussed in
the solution to question 74d2, this ensures that the overridden method behaves (in terms of the exceptions
it may throw) better than the superclass method.
We call this change of exception types during overriding covariant: co- because the subclass method throws a subclass of the exception thrown by the superclass method.
If we remove @Override
from foo()
in B
, add
import java.io.IOException;
to the top of class B
and change the declaration of foo()
in B
to:
void foo() throws IOException {
throw new IOException();
}
then class B
does not compile. The problem is that the compiler still thinks we are trying to override foo
in A
, and tells us that the exception type IOException
that we have specified does not match the exception
type specified for foo
in A
, which is MyExceptionC
.
This tells us that in Java we cannot overload methods based on the types of exceptions they throw.
Finally, the demo class given in the question does not compile because exception handling is checked by the compiler based on the
apparent types of variables. Variable myB
has apparent type A
, even though it is initialised to
have actual type B
. Apparent type A
means that the compiler thinks the call myB.foo()
may
throw an exception of type MyExceptionC
, which is not sufficiently covered by the catch
clause for exceptions
of type MyExceptionD
.
This can be fixed by changing the apparent type of myB
to B
, or by down-casting myB
to type B
at the
call site, or by catching an exception of type MyExceptionC
.