将已知类型的引用转换为类型层次结构之外的接口

假设你有一个像这样的干净课:

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是一个接口,所以有可能存在一个实现GA的子类。 因此,至少可能在运行时存储在工具G中的对象的类型。

“但我刚刚分配a包含A型!愚蠢的编译器!” 重要的是要记住,在评估强制转换的潜在成功时,编译器只查看变量的编译时类型 。 因此,编译器不会试图找出变量中实际存在的内容,而只是查看变量本身的类型。

请注意,如果您A声明为final ,那么这将成为编译时错误,因为现在编译器知道没有A子类,并且A本身不实现G ,因此转换在运行时永远不会成功。

最后,相同的逻辑适用于将CAC不是从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。