Java 转换到类的接口

2022-09-02 19:52:09
public class InterfaceCasting {

    private static class A{}

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

}

编译成功,但运行时异常

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

为什么编译成功?编译器必须知道可序列化不是 A?


答案 1

正如您所指出的,这将编译:

interface MyInterface {}

class A {}

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

但是,不会编译:

interface MyInterface {}

class A {}

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

那么,这是怎么回事呢?有什么区别?

好吧,由于它只是一个接口,很可能由扩展A的类实现,在这种情况下,从到的转换将是合法的。MyInterfaceMyInterfaceA


例如,此代码将在所有执行的 50% 中成功,并说明编译器需要解决可能不可判定的问题,以便始终在编译时“检测”非法强制转换。

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

答案 2

Java 语言规范指出:

有些强制转换在编译时可能被证明是不正确的;此类强制转换会导致编译时错误。

而后面的节目《编译时合法性详细规则》将编译时引用类型 S 的值转换为编译时引用类型 T 的转换——请注意,它们非常复杂且难以理解。

有趣的规则是:

  • 如果 S接口类型:
    • 如果 T 不是最终的类型 (§8.1.1),则如果存在 T 的超类型 X 和 S 的超类型 Y,使得 X 和 Y 都是可证明不同的参数化类型,并且 X 和 Y 的擦除是相同的,则会发生编译时错误。否则,强制转换在编译时始终是合法的(因为即使 T 不实现 S,T 的子类也可能实现)。

在你的例子中,很明显,演员是非法的。但请考虑以下细微变化:

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

现在,在运行时可以从 to 进行强制转换,这表明,在这些情况下,最好由运行时来决定我们是否可以强制转换。SerializableA


推荐