2

我正在查看一些过去的 OOP 试卷,如果能帮助我理解以下代码,我将不胜感激。问题是,给定第一个代码块并且 Sandwich 实现了 Edible,以下哪些语句是合法的?

Sandwich sub = new Sandwich();
Rectangle cerealBox = new Rectangle(20,30,20,10);
Edible e = null;

e = sub;
sub = e;
sub = (Sandwich) e;
sub = (Sandwich) cerealBox;
e = cerealBox;
e = (Edible) cerealBox;
e = (Rectangle) cerealBox;
e = (Rectangle) null;
e = (Edible) sub;
cerealBox = (Rectangle) new Object();

我目前的理解是,第一个语句是正确的,因为 sub 具有构成可食用对象所需的元素,因此它不适用于第二个语句。并且使用第三个语句强制转换确实允许它工作。但第四个不是因为谷物盒不适用于三明治。然后由于演员阵容,最后两个确实有效。但显然第六个有效?

很抱歉我对我所知道的事情的糟糕解释,任何帮助将不胜感激。

4

2 回答 2

6

Java 中的继承和接口实现被称为表示“is-a”关系。也就是说,如果Boy继承自Person,则Boyis-a Person。因此,它可以被视为一个Person因为它是)。这尤其意味着它可以分配给 type 的实例Person

有了这个,我们可以决定

  1. e = sub编译并运行良好。[好的]
  2. sub = e,另一方面,不编译:is-a 关系不能颠倒。[!]
  3. sub = (Sandwich) e 强制上述通过显式转换进行编译。此外,由于e此时确实包含一个三明治(来自分配 1),因此此转换在运行时也会成功。[好的]
  4. sub = (Sandwich) cerealBox – 由于没有从Rectangleto 进行有意义的转换Sandwich,因此无法编译。[!]
  5. e = cerealBox——这里也一样,原因大致相同。[!]
  6. e = (Edible) cerealBox在这里,Java 编译器投降了:它允许强制转换,即使我们(程序员)知道它不能成功,因为包含在其中的对象cerealBox没有实现Edible——但编译器不知道这一点:可以想象,对象包含其中可以派生自Rectangle(因此可分配给cerealBox),也可以实现Edible.

    所以编译器默认了。但在运行时,你会得到一个ClassCastException.

    请注意此语句与 4 的不同之处:在那里,编译器知道cerealBox不能包含Sandwich. 但它可能包含一些东西Edible。为什么?因为 Java 是单继承(并且Rectangle不继承自Sandwich),但允许从多个接口进行扩展。[C]

  7. e = (Rectangle) cerealBox失败,因为强制转换实际上完全没有意义(cerealBox已经是 type Rectangle)并且剩余的语句相当于 5。[!]
  8. e = (Rectangle) null失败,即使null 可以分配给一个Edible对象。但是由于与 5 无效相同的原因,演员表使其无效。[!]
  9. e = (Edible) sub是有效的,并且等价于 1。然而,转换是完全多余的,因为 Java 在继承层次结构中隐式执行向上转换。[好的]
  10. cerealBox = (Rectangle) new Object()编译,并在运行时崩溃。原因类似于6:ObjectRectangle;的基类 因此,一个对象可以包含一个Rectangle实例,这将使该语句成功(有关示例,请参见 3)。

    确实,编译器有点愚蠢——它可以看到被转换的对象——<code>new Object()——绝不可能是Rectangle. 但是编译器不做这个分析,因为在一般情况下这是不可能的。[C]

键:[OK] = 编译,无错误运行。[C] = 编译,崩溃。[!] = 不编译。

正如你所看到的,哪些语句是“合法的”这个问题并没有完全解决这个问题:其中一些语句可以编译,但在运行时会产生异常。他们合法吗?

于 2012-05-14T18:28:22.980 回答
1

6号不行。Rectangle 没有实现 Edible,因此不能转换为它。

编辑:请参阅 Vulcan 的评论,了解 IDE 为何允许它。您将收到运行时错误。

于 2012-05-14T18:10:56.103 回答