11. Inheritance Calls I
A typical multiple choice question designed to assess understanding of inheritance assigns an instance of a subclass to a variable whose type is a superclass and then asks what methods would be called.
Setup
The classes to be used in this discussion are Mammal
and
Human
. These are not supposed to be complete or make perfect
sense. Please forgive the weird temperature
function.
public class Mammal {
public double temperature() { return 123.4; }
}
public class Human extends Mammal {
public double temperature() { return 98.6; }
public void speak(String s) { /* not shown */ }
}
Example A
A Human is assigned to a Mammal variable. Can you call its speak
method?
{
Mammal m = new Human();
m.speak("is this ok?"); // Spoiler: nope, error
}
Discussion 1
The phrasing of this question makes it very confusing for many people. I used to teach this slogan (which is correct and may help some people):
- The true type of a variable determines which method gets
called. In this example, the true type of
m
isHuman
. - The declared type of a variable determines what methods you are
allowed to call. In this example, the declared type if
m
isMammal
.
The thing that bothers me about this approach is that there is no logic to it; it is just a rule to follow.
Discussion 2
Another way of phrasing the same question is to create a separate function for the setup and the “action”. I think this makes the rules of which functions are allowed to be called clearer.
public static void action(Mammal m) {
m.speak("is this ok?"); // (1) Spoiler: nope, error
}
public static void setup() {
Human h = new Human();
action(h); // (2)
}
action
First, an analysis of the action
method. In this method, all you
know about m
is that it is a Mammal
. Since action
is a method,
it can be called from more than one place, so the reader has no reason
to believe that m
is always a Human
. In fact, you should imagine
other places in the code with calls like:
action(new Mammal());
Looking at the problem this way, it should be clear that you cannot
call the speak
method of the m
object.
setup
The other place something can go wrong is at the site where action
is called, labeled (2) in the code. If the h
object is not an
instance of Mammal
, then this would be an error.
Discussion 3
The original variable is unaltered by casting.
{
Mammal m = new Human();
m.speak(); // (1) nope
Human h = (Human) m;
h.speak(); // (2) ok
m.speak(); // (3) still nope
}
Worked Example A
The code Mammal m = new Snake()
corresponds to the setup code below.
This is an error at the call to action because the Snake variable is
not a Mammal.
public static void setup2() {
Snake x = new Snake();
action(x);
}
Exercise Example B
Imagine turning code into a program using classes like Variable
with
subclasses IntegerVariable
and StringVariable
. All Variable
objects have a name
method. IntegerVariable
objects have an add
method to add to the number. StringVariable
objects have a prepend
method that puts text in front of what is already there.
Is the following code legal? Explain.
{
Variable v = new IntegerVariable("a",3); // code for: int a = 3;
Variable w = new StringVariable("s","Turkey"); // code for: String s = "Turkey";
v.add(5); // code for: a += 5;
w.prepend("Republic of "); // code for: s = "Republic of " + s;
}
Exercise Example C
Break the code below into a setup and action step, then analyze it. You have to know how chess pieces move to understand this example. (See links in the following paragraph if you want.)
{
ChessPiece q = new Queen();
q.slide_horizontally(5); // move 5 squares to the right
}
Chess explanation: a queen can move an unlimited amount of squares in
a
line. The
knight is one chess piece does not move in a straight
line. from
this you deduce that although a queen may have a slide_horizontally
method, a generic ChessPiece
should not.
Exercise Example D
Suppose there are classes Mammal, Human, and Dog. All mammals have an eat() method, humans have a talk() method, and dogs have a bark() method. Explain which parts of the following code work, and why explain the problems with the parts that do not work.
public static void setup() {
Human h = new Human();
Dog d = new Dog();
Mammal m = new Dog();
Mammal q = h;
action(h, d, q); // (1)
action(q, h, h); // (2)
}
public static void action(Mammal a, Mammal b, Human c) {
a.talk(); // (3)
a.eat(); // (4)
b.bark(); // (5)
c.talk(); // (6)
}
Exercise Example E
An extended example with many points to wonder about. This was used in a 20 minute in-class discussion.
Warning
This example includes several ideas not covered on this page!Setup E
public class M {
public void f() { f("Mongoose"); }
public void f(String x) { System.out.println(x); }
}
public class P extends M {
public void f(String x) { System.out.println("Pretty "+x); }
public void f(int n) { f("Pink #"+n); }
public void g() { System.out.println("Go for it"); }
}
Questions E, written AP style
public static void questionD() {
M y = new P();
y.g(); // (1)
/* 2 */
{
P py = (P) y; // (2)
}
/* 3 */
if (y instanceof P) {
y.g();
}
/* 4 */
if (y instanceof P) {
P by = (P) y;
// (4a)
py.g();
// (4b)
((P) y).g();
// (4c - only for very advanced)
(P) y.g();
}
/* 5: What happens? */
y.f();
}
Questions E, written function style
This is the same code as above, but split into a setup function and an action function. The variable names have also been changed to show the declared type of the object, which is a strategy to keep track of the information.
public static void setup() {
P p = new P();
action(p);
}
public static void action(M m)
{
m.g(); // (1)
/* 2 */
{
P p = (P) m; // (2)
}
/* 3 */
if (m instanceof P) {
m.g();
}
/* 4 */
if (m instanceof P) {
P p = (P) m;
// (4a)
p.g();
// (4b)
((P) m).g();
// (4c)
(P) m.g();
}
/* 5: What happens? */
m.f();
}