3

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

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

4

6 回答 6

7

强制转换意味着你比编译器更清楚什么是有效的。您是在告诉编译器闭嘴并跟随您的领导。编译器可以在少数情况下判断强制转换无效,但很容易被愚弄。

大多数转换往往是从 Object 到其他东西,例如从非泛型集合中获取对象或使用 PortableRemoteObject.narrow 获取远程对象时。这些转换总是可以编译的,因为无论你转换成什么(只要它是一个对象,而不是原始对象)总是对象的一个​​有效子类。

参考类型转换 (5.5.1)部分中的 Java 语言规范中有一些转换规则。如果编译器可以找出类之间没有关系(编译器可以告诉类是不同的,并且两者都不是另一个的子类),那么它将拒绝强制转换。

添加的示例很有趣,它失败了,因为编译器有足够的信息来判断强制转换是无效的。如果您将代码更改为:

    A a = new A();
    G g = (G)a;
    Object o = a;
    C c = (C)o;

然后它再次编译正常(即使它同样错误)。

于 2013-05-02T20:37:38.850 回答
2

这里的问题是,因为G是一个接口,所以它可能有一个子类Aimplements G。因此,至少有可能在运行时存储在aimplements中的对象的类型G

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

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

最后,同样的逻辑适用于转换CA:C不派生自A,并且编译器知道没有C派生自的子类A(因为它们都派生自C某个点!),因此转换不可能在运行时成功。

于 2013-05-02T20:50:24.367 回答
2

这在编译时是允许的,因为 Java 编译器允许您这样做;它假设你知道你在做什么。

但是,JVM 会计算出来并ClassCastException在运行时抛出 a。

于 2013-05-02T20:38:10.360 回答
1

当然,但这也是有效的:

public class A {}
public class B {}

A a = new A();
((B) a);

你几乎可以将任何东西投射到任何东西上,当你知道你想要什么类型时,你就可以进行投射了。

(但是,当您使用某些类型转换时,例如上面的示例,ClassCastException当 Java 意识到它不会正确转换时,您会在运行时得到一个。)

于 2013-05-02T20:38:30.763 回答
1

编译器不一定知道是否a实现G

强制转换几乎告诉编译器采用这个变量,并将其视为不同的类型。

如果您尝试对强制转换变量进行操作,就好像它已实现G(当它没有实现时),您将收到运行时错误

于 2013-05-02T20:38:46.953 回答
1

只能在运行时检查转换的正确性。因此,在您的示例中,即使对您来说可能很明显,但对于程序来说,直到它实际运行并尝试将变量 a 强制转换为接口 G 时,它才会知道它是不正确的。此时它将抛出 ClassCastException。

于 2013-05-02T20:40:18.963 回答