将已知类型的引用转换为类型层次结构之外的接口
假设你有一个像这样的干净课:
public class A { // Stuff }
和这样的界面:
public interface G { // Stuff }
为什么我被允许这样做:
A a = new A(); ((G) a) // No errors thrown
我无法理解为什么当它们彼此无关时,应该可以从A类转换到接口G. 有人可以向我解释一下吗?
跟进。 如果我做以下事情:
public class C implements G { // Stuff }
这不会编译:
((C) a)
实现接口的类和接口之间有什么区别?
编辑:我得到一个编译器错误说:
无法从A转换为C.
转换意味着你比编译器更了解什么是有效的。 您告诉编译器关闭并跟随您的线索。 在少数情况下,编译器可以告诉强制转换是无效的,但它很容易被愚弄。
大多数强制转换都倾向于从Object到其他东西,例如从非泛化集合中获取对象或使用PortableRemoteObject.narrow获取远程对象时。 这些强制转换始终是编译的,因为无论你投射到什么(只要它是一个Object,而不是一个原语)总是一个有效的Object子类。
在参考类型转换(5.5.1)一节中,Java语言规范中有一些转换规则。 如果编译器可以发现类之间没有关系(编译器可以告诉类是不同的,也不是另一个的子类)那么它将拒绝转换。
添加的示例很有趣,它失败了,因为编译器有足够的信息来告诉强制转换。 如果您将代码更改为:
A a = new A(); G g = (G)a; Object o = a; C c = (C)o;
然后它再次编译好(即使它同样错误)。
这在编译时是允许的,因为Java编译器允许你这样做; 它假设你知道你在做什么。
但是,JVM会将其计算出来并在运行时抛出ClassCastException
。
这里的问题是因为G
是一个接口,所以有可能存在一个实现G
的A
的子类。 因此,至少可能在运行时存储在工具G
中的对象的类型。
“但我刚刚分配a
包含A
型!愚蠢的编译器!” 重要的是要记住,在评估强制转换的潜在成功时,编译器只查看变量的编译时类型 。 因此,编译器不会试图找出变量中实际存在的内容,而只是查看变量本身的类型。
请注意,如果您A
声明为final
,那么这将成为编译时错误,因为现在编译器知道没有A
子类,并且A
本身不实现G
,因此转换在运行时永远不会成功。
最后,相同的逻辑适用于将C
为A
: C
不是从A
派生A
,并且编译器知道C
子类没有从A
派生(因为它们都在某个时候从C
派生!)因此不可能演员阵容可以在运行时成功。
当然,但这也是有效的:
public class A {} public class B {} A a = new A(); ((B) a);
你可以将任何东西投射到任何东西上,当你知道你想要什么类型时,你就可以投射。
(但是当你使用一些强制转换时,例如上面的例子,当Java意识到它不能正确转换时,你会在运行时得到一个ClassCastException
。)
编译器不一定知道是否实现G
Casting几乎告诉编译器接受这个变量,并把它当成一个不同的类型。
如果您尝试对cast变量执行操作,就好像它实现了G
(当它没有)时,您将收到运行时错误
只能在运行时检查转换正确性。 所以在你的例子中,即使你可能很明显,对于程序来说,在它实际运行并尝试将变量a转换为接口G之前,它不会知道它是不正确的。此时它将抛出ClassCastException。