Email address:
Password:
SCJP/OCPJP 1.6 certification
Practice Exams and Training
SCJP Online Training » Discussions

Multiple casts are a bit confusing.

Dear Fred,


interface I{}
class A implements I{ } 
class B extends A{}
class C extends B{}


The following statements are given:

A a=new A();

B b=new B();


1. a=(B)(I)b;

2. b=(B)(I)a;

3. a=(I)b;

4. I i=(C)a;


Please suggest a simple method by which I can recognize which options would lead to a compilation error or a runtime-exception.

The simplest method would be to go from right to left.


For each cast attempt, apply these three rules in that order:


1. When you attempt to cast between an interface and a non-final class, it always compiles.

2. If the reference type of what you are assigning is not in the same hierarchy root as of the type to which you cast it, you get a compile error. (To be in the same hierarchy root, one of the two types needs to be a sub-type of the other type).

3. If the actual value (object) you are assigning is not null and not the same or a sub-type of the class to which you cast it, you get a runtime error.


Let's take a look at some of these codes:


B b=new B();
a=(B)(I)b;


All we have to do is go from the right-to-left order, and match each cast attempt with the three rules I mentioned above.


So here is our first cast attempt:


a=(B)(I)b;

We know I is an interface, and the reference type of the variable b is B, which is a non-final class. So it matches with the first rule, which says it always compile. So as we know it compiles, we don't need to check it with the second rule. The third rule checks against the possibility of getting a runtime exception. We know that the actual object b contains is an object of B (as we can see it in the first line). We also know B is a sub-type of I because B extends A and A implements I. Therefore, you wouldn't get a runtime error at this line. So for this cast attempt, you don't get any compile error and you don't get any runtime exception.


Moving to the second cast attempt on the same line:


a=(B)(I)b;


We know I is an interface, and B is a non-final class, so it compiles according to the first rule. To check against the rule#3 we need to know the actual object. The actual object is still what the variable b contained, because this is the only value we have in this whole statement, and that is being casted at this part. Variable b contains an object of the class B, which is the same type we cast it to in this part. Therefore, according to the third rule, we don't get a runtime error either. That's it - for this whole statement, you don't get a compile error or runtime error.



Let's consider another statement:


b=(B)(I)a;


Going from the right-to-left order, we have this part as our first casting attempt:


b=(B)(I)a;

Rule #1 makes it compile. Actual object a contains is an object of the class A, which is a sub-type of the interface I, which makes it okay at the runtime by the rule #3.


b=(B)(I)a;

Rule #1 still makes it compile. The actual object we are casting here is what the variable a contained. We know it is an object of the class A, which is NOT a sub-type of the class B. According to the rule #3, this makes it fail at the runtime as it throws a ClassCastException.



The last statement:

I i=(C)a;


Taking the first (and only) cast attempt:

I i = (C)a;

The reference type of the variable a is A. Neither C nor A is an interface and thus the first rule does not apply. Since C is a sub-type of A , these two are in the same hierarchy root, and so it compiles. Checking against the rule #3, the actual object a contains is an object of class A, which is NOT a sub class of C. Thus, it throws a ClassCastException at the runtime.



A little exercise for you:


Can you figure it out with this statement?:

I i = (A)(C)(B)b;

I i = (A)(C)(B)b;


1. Rule 2 applies here. It compiles successfully because the reference types are in the same hierarchy.


I i = (A)(C)(B)b;


2. B and C are in the same hierarchy so it compiles but the object of B is not a sub-type of C so it fails at run-time.


Please correct me if I am wrong.


In the 3rd option of my question:

a=(I)b


b is a non-final class and I is an interface in the same hierarchy. So it will compile but the object of b is not a sub-type of A so that leads to a compilation failure?

You solved the exercise question correctly.


As for the 3rd option in your question, it of course fails at compilation, but not because of casting, but because of incompatible assignment types.


You said: "the object of b is not a sub-type of A so that leads to a compilation failure"


In the three rules I mentioned, there is no way the actual object would fail the compilation. Compilation fails only if the reference types were not in the same hierarchy.


Like I said, the compilation fails here due to the incompatible assignment types:


a=(I)b


In the above statement, the 'type' of the right side operand (the part at the right hand side to the equal operator) is I. This is because as the casting happens from right-to-left, the left-most cast type will be the final type of the whole expression. In that sense, you are trying to assign a reference with type of I to a reference variable with type A, which always fail for I not being a sub type of A.


In other words, you could always check the assignment compatibility at first before checking into the casting statements. For instance, consider having a complex statement like this:


a = (I)(A)(I)((B)(C)(D)(E)b);


If you just check the left-most cast type, which is I, you would know it fails at the compilation because of incompatible assignment. Knowing it fails at compilation, you don't need to worry about the rest of the types used.

I got it now :) thanks.

ExamLab © 2008 - 2024