Java casting in interfaces

JavaInterfaceCasting

Java Problem Overview


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 !!
     }
}

Java Solutions


Solution 1 - Java

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
     }
}

Solution 2 - Java

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.

Solution 3 - Java

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.

Solution 4 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser711189View Question on Stackoverflow
Solution 1 - JavaWilQuView Answer on Stackoverflow
Solution 2 - JavamabaView Answer on Stackoverflow
Solution 3 - JavaÉtienne MiretView Answer on Stackoverflow
Solution 4 - JavaBuhake SindiView Answer on Stackoverflow