Java casting in interfaces
Can someone please explain to me how the compiler does not complain in the first casting, but does complain in the second?
interface I1 { }
interface I2 { }
class C1 implements I1 { }
class C2 implements I2 { }
public class Test{
public static void main(String[] args){
C1 o1 = new C1();
C2 o2 = new C2();
Integer o3 = new Integer(4);
I2 x = (I2)o1; //compiler does not complain
I2 y = (I2)o3; //compiler complains here !!
}
}
When you cast o1
and o3
with (I2)
, you tell the compiler that the class of the object is actually a subclass of its declared type, and that this subclass implements I2
.
The Integer
class is final, so o3
cannot be an instance of a subclass of Integer
: the compiler knows that you're lying. C1
however is not final, so o1
could be an instance of a subtype of C1
that implements I2
.
If you make C1
final, the compiler will complain too:
interface I1 { }
interface I2 { }
final class C1 implements I1 { }
class C2 implements I2 { }
public class Test{
public static void main(){
C1 o1 = new C1();
C2 o2 = new C2();
Integer o3 = new Integer(4);
I2 y = (I2)o3; //compiler complains here !!
I2 x = (I2)o1; //compiler complains too
}
}
According to JLS chapter 5
5.5.1. Reference Type Casting
Given a compile-time reference type S (source) and a compile-time reference type T (target), a casting conversion exists from S to T if no compile-time errors occur due to the following rules. If T is an interface type:
If S is not a final class (§8.1.1), then, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs.
Otherwise, the cast is always legal at compile time (because even if S does not implement T, a subclass of S might).
If S is a final class (§8.1.1), then S must implement T, or a compile-time error occurs.
That's because class Integer
is final and C1
is not. Thus, an Integer object cannot implement I2, while a C1 object could if it is an instance of a subclass of C1 that implements I2.
According to JLS 5.5.1 - Reference Type casting, the rule(s) apply:
-
If T is a class type, then either |S| <: |T|, or |T| <: |S|. Otherwise, a compile-time error occurs.
I2 y = (I2)o3; //compiler complains here !!
In this case, an Integer
and I2
are unrelated in any way, so a compile-time error occurs. Also, because Integer
is final
, there is no relation between Integer
and I2
.
I2
and I1
can be related due to both being a marker interface (there are no contract).
As for the compiled code, the rule follows:
- If S is not a final class (§8.1.1), then, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs.
S
is o1
and T
is I2
.
Hope this helps.