Java Casting Interface to Class

后端 未结 8 1259
闹比i
闹比i 2020-12-05 08:03
public class InterfaceCasting {

    private static class A{}

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


        
相关标签:
8条回答
  • 2020-12-05 08:19

    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.

    0 讨论(0)
  • 2020-12-05 08:33

    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

    0 讨论(0)
  • 2020-12-05 08:35
    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.

    0 讨论(0)
  • 2020-12-05 08:41

    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 ....
    
    0 讨论(0)
  • 2020-12-05 08:41

    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.

    0 讨论(0)
  • 2020-12-05 08:41

    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;
    }
    
    0 讨论(0)
提交回复
热议问题