18
public class InterfaceCasting {

    private static class A{}

    public static void main(String[] args) {
        A a = new A();
        Serializable serializable = new Serializable(){};
        a = (A)serializable;
    }

}

Compilation succeed but Runtime exception

Exception in thread "main" java.lang.ClassCastException: InterfaceCasting$1 cannot be cast to InterfaceCasting$A

WHY COMPILATION SUCCEED? Compiler must known that serialiazable is not A?

1
  • i think you have to review concepts on Java Exception... Commented Aug 27, 2010 at 9:30

8 Answers 8

28

As you point out, this will compile:

interface MyInterface {}

class A {}

public class InterfaceCasting {
    public static void main(String[] args) {
        MyInterface myObject = new MyInterface() {};
        A a = (A) myObject;
    }
}

This however, will not compile:

interface MyInterface {}

class A {}

public class InterfaceCasting {
    public static void main(String[] args) {
        A a = (A) new MyInterface() {}; // javac says: "inconvertible types!"
    }
}

So, what's going on here? What's the difference?

Well, since MyInterface is simply an interface, it could very well be implemented by a class that extends A, in which case the cast from MyInterface to A would be legal.


This code for instance, will succeed in 50% of all executions, and illustrates that the compiler would need to solve possibly undecidable problems in order to always "detect" illegal casts at compile time.

interface MyInterface {}

class A {}

class B extends A implements MyInterface {}

public class InterfaceCasting {
    public static void main(String[] args) {
        MyInterface myObject = new MyInterface() {};
        if (java.lang.Math.random() > 0.5)
            myObject = new B();
        A a = (A) myObject;
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Is this also possible without extending A ? Because that is what I need.
How do you even instantiate an interface like new MyInterface(){};? This makes no sense.
@The_Martian, the {} in the end of the line creates an anonymous class implementing the interface. Try it! :-)
10

Java language specification states, that:

Some casts can be proven incorrect at compile time; such casts result in a compile-time error.

And later on the show The detailed rules for compile-time legality of a casting conversion of a value of compile-time reference type S to a compile-time reference type T - beware, they are very complex and hard to understand.

The interesting rule is:

  • If S is an interface type:
    • If T is a type that is not final (§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 T does not implement S, a subclass of T might).

In your example, it's perfectly clear, that the cast is illegal. But consider this slight change:

public class InterfaceCasting {

    private static class A{}
    private static class B extends A implements Serializable{}

    public static void main(String[] args) {
        A a = new A();
        Serializable serializable = new B(){};
        a = (A)serializable;
    }    
}

Now a cast from a Serializable to A is possible at runtime and this shows, that in those cases, it's better left to the runtime to decide if we can cast or not.

1 Comment

yeah , I got it now Thanks everybody for your help
4
Serializable serializable;
a = (A)serializable;

As for the compiler, the variable serializable can contain any object that implements Serializable, which includes subclasses of A. So it assumes that you know that the variables indeed contains an A object and allows that line.

3 Comments

No, it may not include A. The compiler knows for sure that A doesn't implement Serializable.
@aioobe yes, but a subclass of A may
you should clarify this by writing "which includes subclasses of A".
1

The compiler is not smart enough to trace the origins of serializable and realize that it can never be of type A. It really only evaluates the line:

a = (A)serializable;

and sees that serializable a reference of type Serializable but it may reference a class that also is of type A. The actual class that serializable references is not known until run-time.

In this trivial case, we know that this cast will never succeed, but in general this is left as a run-time issue as the different code paths that may lead to a casting are (in theory) infinite.

If you want to avoid this issue at run-time you could test for it..

if (serializable instanceof A) {
    a = (A)serializable;
} else ....

Comments

0

It can't know that because the compile time type of serializable is Serializable.

To illustrate, consider this:

private static class A{}
private static class B implements Serializable {}

Serializable serializable = new B();
A a = (A)serializable;

this is exactly like your question, it compiles.

private static class A{}
private static class B implements Serializable {}

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

this does not compile because b is not an A.

Comments

0

While I don't know the correct answer, it's usually not a good idea to cast an interface to a class, for several reasons.

a) An interface defines a contract, it guarantees behavior. A class may define more than this contract, usage of the other methods may have unexpected side-effects and break APIs. E.g. when a method is passed a list and you find out the passed object is actually a LinkedList and you cast it and use the Queue based methods it also defines, you are breaking the API.

b) also, the object with the interface may not be a "real" object at runtime, but perhaps a service proxy created around the original object by a library such as Spring or EJB. Your cast will fail in those cases.

If you absolutely must cast, never do it without an instanceof check:

if(myServiceObject instanceof MyServiceObjectImpl){
    MyServiceObjectImpl impl = (MyServiceObjectImpl) myServiceObject;
}

Comments

0

The detailed rules for compile-time legality of a casting conversion of a value of compile-time reference type S to a compile-time reference type T are as follows:
[...]
If S is an interface type:
- If T is an array type, [...].
- If T is a type that is not final (§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 T does not implement S, a subclass of T might).

Source :
JLS : Conversions and Promotions

Comments

-1

Serializable is NOT an A, so it throws ClassCastException.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.