C Sc 335 3-April-12 Inheritance Heck

Offered to help you prepare for this week's section and test 2 on 10-April-2012, by Ursula McDuffee, Jacob Pitts, Richard Snodgrass, and Rick Mercer

First, let's muddle through an example test question. This is followed by tips that summarize the recommended process, which is followed by the answers to this question:

6. Given this Meaningless Foo Inheritance Hierarchy, answer the questions that follow.

////////////////////////////////////

class Foo extends {

public void one() {

System.out.println("Foo1");

}

public void three() {

System.out.println("Foo3");

}

}

////////////////////////////////////

class Bar extends Foo {

public void one() {

System.out.println("Bar1");

}

public void two() {

System.out.println("Bar2");

one();

}

}

////////////////////////////////////

class Baz extends Foo {

public void one() {

System.out.println("Baz1");

super.three();

}

public void two() {

System.out.println("Baz2");

}

}

////////////////////////////////////

class Goo extends Bar {

public void one() {

super.one();

System.out.println("Goo1");

}

public void two() {

System.out.println("Goo2");

}

public void three() {

super.two();

System.out.println("Goo3");

}

}

6a) Using the Foo inheritance hierarchy on the preceding page, write a UML diagram of the inheritance hierarchy. Do not include the class Object. The diagram must include a) all class names, b) all methods for each class (no methods from class Object needed), and arrows to indicate what which class extends which. (10pts)

6b) Complete the following table to show the behavior of all messages. It will help answer the actual questions. Use / to indicate new lines. If an instance will not understand the message, write NA instead of output. (16pts)

methods ß classesÞ / Foo / Bar / Baz / Goo
one
two
three

6c) For each blank line, write one of these three choices 1) The actual output if the code compiles and runs (should be seen above). Use / to indicate a new line. 2) CT (Compile Time error) if an error would occur at compile time, or 3) RE (Runtime Error) if an error while the program is running. (16pts)

Foo b = new Baz();

Foo c = new Bar();

Bar d = new Goo();

Object e = new Baz();

b.one(); ______

c.one(); ______

c.two(); ______

e.one(); ______

((Baz)c).two(); ______

((Goo)d).three(); ______

((Foo)e).one(); ______

((Goo)((Foo)e)).one(); ______

Inheritance Heck Tips

“Inheritance Heck” (previously named a slightly different name) refers to seemingly complex and contradictory behavior by the compiler and runtime system when resolving types and methods in Java programs. However, through a proper understanding of how the compiler does type checking and how the runtime system works, it is possible to be able to correctly predict the behavior of even the most complex examples. Here is a recommended process that should help.

1) First consider how the compiler will react

  1. If, based on the compile-time type of the variable, can we cast that type to that type (if there is a cast involved)?
  2. And, once we do that cast, does that class type have a .method() method, either directly, or inherited from one of its superclasses, found by traversing up the inheritance tree?

If yes to both, the code compiles. If either fails write CE for CompileTime Error

c.two(); ____CE______


2) If the message compiles consider the following that could happen at runtime

a.  Based on the run-time type, which is the type of the actual object rather than the type into which it is stored, can that object be cast to the variable's class type?

If yes, some message will be sent (code will run). Otherwise write RE for Runtime Error. The only time the answer would be "no" at runtime would be if you try to cast too far down the tree.

((Baz)c).two(); __RE ______This is a ClassCastException

3) If there is no CE or RE, determine the output (answers can be selected from the table):

The Java runtime starts with the run-time type. It finds which version of the method it should use by traversing back up the inheritance tree. It will always find the method, eventually.

b.one(); ___ Baz1 / Foo3__

BTW: For static methods, the Java runtime starts with the compile-time type. Thus, the specific static method is figured out entirely by the compiler (there is no dynamic binding needed). Examples: Math.sqrt(double) or Integer.parseInt(String)


Compile Time Rules:

You can assign up the inheritance hierarchy

Foo b = new Bar();

Object o = new String("abc");

but not down the inheritance hierarchy

Goo g = new Bar(); // Type mismatch: cannot convert from Bar to Goo

String str = new Object(); // Type mismatch: cannot convert from Object to String

Casting up or casting down is okay

((Object) anInt).toString();

((Integer) obj).toString();

((Foo) c).one();

((Bar) b).one();

However, casting sideways is not okay Object

Integer Double

((Integer) aDouble).toString(); // Cannot cast from Double to Integer

((Double) anInt).toString(); // Cannot cast from Integer to Double

Runtime Rule

Casting below object's runtime type is not okay. Foo

Bar Baz

Goo

Foo f = new Bar();

((Goo)f).one(); // Bar cannot be cast to Goo (it does compile)

1