I was attempting some practice exams for my Java final coming up and I came across this question.
Consider the following class definitions and indicate whether 'Test.main()' would compile successfully. If it does compile, indicate whether it would run successfully or if not, indicate what Exception would be thrown.
public class A {
public int method(int[] a) {...}
}
public class B extends A {
@Override
public int method(int[] a) {...}
}
public class C extends B {
@Override
public int method(int[] a) {...}
public void otherMethod() {...}
}
public class Test {
public static void main(String[] args) {
A a = new C();
B b = new B();
b = (B) a;
}
}
I thought that Test.main() would compile but throw a runtime exception due to the fact that a is of actual type C and we are trying to cast it to type B. This is not the case as the answers say this is fine.
I'm pretty confused about the rules of casting where there is a hierarchy deeper than 2 levels involved. The lecture slides don't really have this kind of information!
So what are some hardfast "rules" to keep in mind if this type of question pops up on the exam?
When there is a complicated hierarchy, try drawing it out so it's clearer:
A <- B <- C
I thought that Test.main() would compile but throw a runtime exception due to the fact that a is of actual type C and we are trying to cast it to type B.
a
's underlying type is C
. However, C
is convertible to B
and A
because C
inherits from B
and B
inherits from A
.
Basically, a general rule for whether a cast of reference types succeeds or not is as follows:
For any cast in the below format:
(X)Y
where
X
is a reference type andY
is a variable of a reference type, the cast will succeed at runtime if you can go fromY
's underlying type toX
in the inheritance hierarchy by only going along the directions of the arrows.
Say we have this code:
A a = new A();
B b = (B)a;
This will fail because we need to go against the direction of the arrows to go from A
to B
How do you know whether a cast will fail at compile time, then?
This is very easy. Just check whether Y
's variable type (not underlying type!) is unrelated to X
.
For example:
// these two types are unrelated
class Foo {}
class Bar {}
// ...
Foo f = new Foo();
Bar b = (Bar)f; // fails to compile
However, if Y
's variable type is related to X
, it compiles fine:
Object f = new Foo();
Bar b = (Bar)f; // Bar inherits from Object, so compiles fine.
// But since Foo (f's underlying type) is unrelated to Bar
// this crashes at runtime