I’m looking at some past exam papers for OOP and I would appreciate any help with understanding the following code. The question is, given this first block of code and that Sandwich implements Edible, which of the following statements are legal?
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();
My current understanding is that the first statement is true because a sub has the required elements to make up an edible Object, hence it does not work the other way round for the second statement. And with the third statement casting does allow this to work. But the fourth doesn’t because cerealBox doesn’t apply to Sandwich. Then the last two do work because of the casting. But apparently the 6th one works?
Sorry about my terrible explanation of what I know, any help would be appreciated.
Inheritance and interface implementation in Java are said to represent an “is-a” relationship. That is, if
Boyinherits fromPerson, thenBoyis-aPerson. Consequently, it can be treated as if it were aPerson(because it is). This in particular implies that it can be assigned to an instance of typePerson.Armed with this, we can decide that
e = subcompiles and runs fine. [OK]sub = e, on the other hand, doesn’t compile: an is-a relationship cannot be reversed. [!]sub = (Sandwich) eforces the above to compile through an explicit cast. Furthermore, sinceeat that point does contain a sandwich (from assignment 1), this cast also succeeds at runtime. [OK]sub = (Sandwich) cerealBox– since there is no meaningful conversion fromRectangletoSandwich, this fails to compile. [!]e = cerealBox– Same here, for much the same reason. [!]e = (Edible) cerealBoxHere, the Java compiler capitulates: it allows the cast, even though we (the programmers) know that it can’t succeed since the object contained incerealBoxdoesn’t implementEdible– but the compiler can’t know this: conceivably, the object contained in it could be derived fromRectangle(and hence assignable tocerealBox), and also implementEdible.So the compiler acquiesces. But at runtime, you’ll get a
ClassCastException.Note how this statement differs from 4: there, the compiler knew that
cerealBoxcouldn’t contain aSandwich. But it could contain somethingEdible. Why? Because Java is single-inheritance (andRectangledoesn’t inherit fromSandwich), but allows extending from multiple interfaces. [C]e = (Rectangle) cerealBoxfails, since the cast is actually completely meaningless (cerealBoxis already of typeRectangle) and the remaining statement is equivalent to 5. [!]e = (Rectangle) nullfails, even thoughnullcould be assignable to anEdibleobject. But the cast makes this invalid for the same reason as 5 is invalid. [!]e = (Edible) subis valid, and equivalent to 1. The cast however is completely redundant, since Java performs casts upwards in the inheritance hierarchy implicitly. [OK]cerealBox = (Rectangle) new Object()compiles, and crashes at runtime. The reason is similar to 6:Objectis a base class ofRectangle; hence, an object could contain aRectangleinstance, which would make this statement succeed (see 3 for an example of that).True, the compiler is kind of stupid – it could see that the object being cast –
new Object()– in no way could be aRectangle. But the compiler doesn’t do this analysis, since it’s impossible in the general case. [C]Key: [OK] = compiles, runs error-free. [C] = compiles, crashes. [!] = doesn’t compile.
As you can see, the question which statements are “legal” doesn’t quite cut it: some of these statements compile, but yield an exception at runtime. Are they legal?