解释这个关于对象引用转换的输出?

时间:2014-04-02 15:10:17

标签: java interface casting

interface I {
}
class A {
}
class B {
}
public class Test {
    public static void main(String args[]) {
        A a = null;
        B b = (B)a; // error: inconvertible types

        I i = null;
        B b1 = (B)i;
    }
}

我知道为什么a无法投放到B,因为B不是从A继承的。
我的问题是,为什么允许B b1 = (B)i;,因为B不是I的实现?
为什么B b1 = (B)i;此行不会强制运行时异常,因为i为空?

6 个答案:

答案 0 :(得分:11)

编译器不了解您的类层次结构的所有内容。就其而言,可能有class B2 extends B implements I,在这种情况下,上述演员需要工作。

答案 1 :(得分:3)

它是一个Narrowing Reference Conversion ..根据java规范$5.1.5允许以下内容。

  

从任何接口类型J到任何非最终的非参数化类型C。

此转换不会显示任何编译时错误,但会在运行时进行检查。如果使用与B无关的适当类初始化接口,它将根据$5.5.3抛出ClassCastException但是它现在没有抛出,因为接口为null并且我们知道

  

可以将null类型的值(空引用是唯一的此类值)分配给任何引用类型,从而产生该类型的空引用。

N.B。 ..我没有被承认所有上述条款......我只是觉得有兴趣在看到这个问题后进行搜索。如果我的发现错了或我误解了任何事情,请纠正我

答案 2 :(得分:3)

当编译器遇到B b = (B)a;时,它毫无疑问地知道aA类型而合法地投放到B

但是,当编译器正在编译B b1 = (B)i;时,它只知道i是一个实现I的对象 - 它可能是:

class C extends B implements I {
}

因此,B 的演员可以合法,因此不能将其视为错误。

这实际上只是Jaffrey's answer的重新陈述,更多的叙述。

答案 3 :(得分:2)

考虑:

interface I { }
class A { }
class B { }
class A2 extends A implements I { }
class B2 extends B implements I { }

void castToB(I i) {
    B b1 = (B)i;  // (HERE)
}

void foo() {
    A2 a2 = new A2();
    castToB(a2);  // legal, since a2 implements I, but the cast to B will throw an
                  // exception at runtime
    B2 b2 = new B2();
    castToB(b2);  // legal, since b2 implements I; the cast to B now succeeds, since
                  // a B2 is a B
}

这表明为什么标记为HERE的行中的演员必须合法,因为有些情况下它可以成功。只有在绝对不可能成功的情况下,Java才会将演员标记为错误。

答案 4 :(得分:1)

我相信其他答案缺少一个重要的细节:这是可能的,因为编译器根本不打算查看接口。

应该用来解释这个问题的论据已被表述为

  

可能有class B2 extends B implements I

  

编译器在此知道AB不相关

这些都是正确的,但它并不能解释为什么编译器知道这两个类不相关而且为什么它不能知道界面已经实现。

最后,这两种情况应该是相似的:如果它可以在编译时检查两个类是否有关系,那么它应该能够在编译时检查是否在该类中实现了一个接口这些情况在编译时可以获得它们的信息。

因此,我同意the comment of Hot Licks中的the duplicate

  

由于。虽然Cat可能不会直接实施家具,但Cat的一些超类可能会。并且编译器选择不深入挖掘这些丑陋的细节,因为接口规则非常复杂。 (谢谢你,Sun。)并且没有一般规则要求编译器检测不可避免的运行时异常(例如除以零) - 它更像是它提供的“服务”。

在编译时检查接口应该是可能的,编译器没有完成,因为我们只能猜测。 ,这些在评论中讨论。

在下面的评论中进行了广泛(非常有趣)的讨论之后,我相信这已经被澄清了。

检查两个类是否有关系是容易和便宜的,您只需查看它们的层次结构。另一方面,接口要求您了解项目中的所有类和接口(包括库等外部资源),以确定接口和类之间是否存在关系。

这使我得出的结论是,虽然它在理论上是可行的,但它不可行,因为它需要两件事:

  1. 重新编译源代码和所有包含的库。
  2. 非常昂贵的支票,必须通过每个类的层次结构。

答案 5 :(得分:-1)

以下是关于Upcasting和Down Cast的一些很好的信息。 http://forum.codecall.net/topic/50451-upcasting-downcasting/

因为每个Class都继承自Object,所以您始终可以向上转换为对象并向下转换为其他类型(B)(Object)a。这不是一个好主意,但它是有效的。如果您实际实例化了A,那么您将获得运行时异常java.lang.ClassCastException,就像您实例化I i = new I(){}的实例

一样
interface I {
}
class A {
}
class B {
}
class Test {
    public static void main(String args[]) {
        A a = null;
        B b = (B)(Object)a;

        I i = null;
        B b1 = (B)i;
    }
}