类的Java转换接口

类的Java转换接口,java,casting,Java,Casting,编译成功,但运行时异常 public class InterfaceCasting { private static class A{} public static void main(String[] args) { A a = new A(); Serializable serializable = new Serializable(){}; a = (A)serializable; } } 为什么编译成功?编译

编译成功,但运行时异常

public class InterfaceCasting {

    private static class A{}

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

}

为什么编译成功?编译器必须知道serialiazable不是一个?

Serializable
不是一个
A
,所以它抛出
ClassCastException
它不能知道,因为
Serializable
的编译时类型是
Serializable

举例说明,考虑一下:

Exception in thread "main" java.lang.ClassCastException: InterfaceCasting$1 cannot be cast to InterfaceCasting$A
这正是你的问题,它编译

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

Serializable serializable = new B();
A a = (A)serializable;
这不会编译,因为
b
不是
A

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

但是,此不会编译:

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

因为
MyInterface
只是一个接口,它很可能由一个扩展了a的类来实现,在这种情况下,从
MyInterface
a
的转换是合法的


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

interface MyInterface {}

class A {}

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

至于编译器,变量serializable可以包含实现
serializable
的任何对象,其中包括
A
的子类。因此,它假设您知道变量确实包含一个
A
对象,并允许该行。

编译器不够聪明,无法跟踪
可序列化的
的起源,并意识到它永远不可能是
A
类型。它实际上只计算行:

Serializable serializable;
a = (A)serializable;
并看到
serializable
类型为
serializable
的引用,但它可能引用类型为
a
的类。
serializable
引用的实际类在运行时之前是未知的

在这种简单的情况下,我们知道这种转换永远不会成功,但一般来说,这是一个运行时问题,因为可能导致转换的不同代码路径(理论上)是无限的

如果您想在运行时避免此问题,可以对其进行测试

a = (A)serializable;

虽然我不知道正确的答案,但出于几个原因,向类强制转换接口通常不是一个好主意

a) 接口定义了契约,它保证了行为。一个类可能定义的不仅仅是这个契约,其他方法的使用可能会产生意想不到的副作用并破坏API。例如,当一个方法被传递一个列表,而你发现传递的对象实际上是一个LinkedList,你将其转换并使用它定义的基于队列的方法,你就破坏了API

b) 此外,具有接口的对象在运行时可能不是“真实”对象,但可能是由库(如Spring或EJB)围绕原始对象创建的服务代理。在这种情况下,你的演员阵容将失败

如果您必须强制执行,请不要在没有instanceof check的情况下执行此操作:

if (serializable instanceof A) {
    a = (A)serializable;
} else ....
编译时引用类型S的值转换为编译时引用类型T的转换的编译时合法性的详细规则如下:
[…]
如果S是接口类型:
-如果T是数组类型,[…]。
-如果T是非最终类型(§8.1.1),则如果存在T的超类型X和S的超类型Y,使得X和Y都是可证明不同的参数化类型,并且X和Y的擦除相同,则会发生编译时错误。否则,强制转换在编译时总是合法的(因为即使T不实现S,T的子类也可能实现)

来源:
声明:

有些强制转换在编译时被证明是不正确的;这样的强制转换会导致编译时错误

随后,我们将展示编译时引用类型S的值转换为编译时引用类型T的强制转换的编译时合法性的详细规则-注意,它们非常复杂且难以理解

有趣的规则是:

  • 如果S接口类型:
    • 如果T非最终的类型(§8.1.1),则如果存在T的超类型X和S的超类型Y,使得X和Y都是可证明的不同参数化类型,并且X和Y的擦除相同,则会发生编译时错误否则,强制转换在编译时总是合法的(因为即使T不实现S,T的子类也可能实现)
在你的例子中,很明显,演员阵容是非法的。但是考虑一下这个细微的变化:

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

现在可以在运行时将
Serializable
转换为
a
,这表明,在这些情况下,最好由运行时决定是否可以转换。

我认为您必须回顾Java异常的概念……不,它可能不包括
a
。编译器肯定知道
A
没有实现
Serializable
@aioobe是的,但是
A
的子类可能你应该通过写“其中包括A的子类”来澄清这一点。是的,我现在明白了,谢谢大家的帮助。这也可以不扩展A吗?因为这正是我所需要的。您甚至如何实例化像new MyInterface(){};这样的接口;?这毫无意义。@u Martian,这行末尾的
{}
创建了一个实现接口的匿名类。试试看!:-)
a = (A)serializable;
if (serializable instanceof A) {
    a = (A)serializable;
} else ....
if(myServiceObject instanceof MyServiceObjectImpl){
    MyServiceObjectImpl impl = (MyServiceObjectImpl) myServiceObject;
}
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;
    }    
}