Objects, Inheritance and Polymorphism


Object Semantics

Suppose we have a simple class Foo:

class Foo {
    private int theValue;
    public Foo(int v) {theValue = v;}
    public int getValue() {return theValue;}
    public void setValue(int v) {theValue = v;}
}

Foo x = new Foo(3);
Foo y = new Foo(5);
x.setValue(2);        // x is now 2
x = y;                // x is now 5
x.setValue(3);        // x and y are now 3. Why does y change?

It is important to understand why y changes at the end. The local variables x and y do not represent two different locations for the Foo objects; instead, they represent pointers to the objects. After the line x=y, x and y point to the same object.



Polymorphism

Here is a fundamental example about classes and so-called inheritance, also in the demo example poly.zip. The Child class "extends" the class Base. However, a Child object permanently retains its identity, even when it is assigned to a Base variable (as in b2 = new Child(4,5)). When the get() method is called, it is the Child get method, not the Base get method.

using System;
using System.IO;

class Demo {
    public static void main(String[] args) {
	Base b1 = new Base(3);
	Base b2 = new Child(4,5);
	System.out.printf("b1.get() = %d, b2.get =%d%n", b1.get(), b2.get());
    }

}

class Base {
    private int x;
    public int get() {return x;}
    public Base(int X) {x = X;}
}

class Child extends Base {
    private int y;
    public int get() {return super.get()+y;}
    public Child (int X, int Y) {super(X); y=Y;}
}

b1.get() returns 3; there is not much else it could return. However, b2.get() returns 9. An observer who reasoned that b2 was of type Base might try to invoke Base.get(), which would return 4.

The "polymorphism" behavior depends on get being declared in class Base, but then "redeclared" in class Child. The most specific version available at runtime is invoked; if this were not the case, then the call to b2.get() would use "static" typing: b2 has type Base, so Base.get is called, and the result would be 4, not 9. Static typing is not what we want!

This behavior is sometimes also called "dynamic dispatch". When we call b1.get(), the system at runtime looks up b1's "dispatch table" and invokes the get() method present in that table. The table itself depends on how b1 was created: with new Base(3) or new Child(4,5).

Note also that there's a violation of one traditional type rule. A Child object is not the same as a Base object, so how do we allow assignments of the form Base = Child? And, if that, why do we not allow Child = Base? The answer is that a Child "is a" Base in the sense that a Child meets all the interface rules of a Base. In this case, the Base class defines a get() method, and a Child object has a get() method too, with the same semantics. A Child can be substituted, in other words, anywhere the program expects a Base, with no possibility of mismatch. The reverse is not true; a Child class could define a new method not present in Base. (Note that if the Base class defines a method not present in Child, then the Child simply inherits that method unchanged.)

toString

System.out.println() is polymorphic: when you give it a non-string type, it calls the object's toString() method to convert it to a string. We saw this in the ratio.java demo.

Ratio.toString can be called explicitly whenever needed, as r.toString(). However, note that toString() works more generally, using inheritance. The master parent class Object defines toString(); any subclass can override that definition, as is being done here. In WriteLine(), when an Object needs to be printed then toString() is called implicitly; the rules of inheritance ensure that the most specific version of toString is the one to be invoked.

Shapes

One classic application of this is with drawing shapes. There is a base class Shape, and then child classes for each specific shape: Triangle, Rectangle, Circle, etc. The base class has a draw() method, but it does nothing (in fact, it is usually labeled abstract, meaning it must be supplied by a subclass). If we have a list shapeList of Shape objects (that is, a picture!) we can draw it as follows:

    for (Shape s : shapeList)
        s.draw();

We do not have to check each shape s for what kind of shape it is.

There are two issues here:

GUIs

Suppose we want to assemble a graphical interface. We have a window w, and a few buttons b1, b2 and b3, and some kind of slider control sc. How can we organize all this?

One approach is to have a library routine clickHappened() that tells us when a mouse event occurred, and where. If we figure out that button b1 was clicked, we look up the action that b1 was supposed to trigger, and execute it. This will involve a lot of code like

if (b1 was clicked) execute_b1_action();
else if (b2 was clicked) execute_b2_action();    ...

But the object-oriented approach is very different:

Now it all works! When b1 is clicked on, the framework invokes B1.onClick() without any effort on our part.

Some variations allow us to pass an OnClick() method to each object without actually defining an explicit child class:

Button savebutton = new Button(position, save_file_method);

Zuul

The Game of Zuul came from a Java implementation in Objects First with Java by David Barnes and Michael Kölling. Polymorphism is used extensively to create different puzzle-room classes that behave differently from the default Room class.

In the Game class, the main game loop is as follows:
        while (! finished) {
            Command command = parser.getCommand();
            finished = processCommand(command);
        }
The processCommand() function understands some universal commands such as "quit", "help", "inventory" (listing the things you have with you) and "look" (listing the things visible in the current room, although the room is able to influence the visibility). But everything else is processed by the room itself, via the Room.respond() method. A subclass of Room can in effect redefine respond(), and thus create new behavior.

Overview of basic classes by file

Game.java: contains the main game loop, above

Parser.java, Command.java, commandWord.java: defines the basic mechanism for reading commands

Item.java and Light.java: Items are things you can carry around with you, like swords and flashlights. Some Items have the special property that they can be "lighted"; some rooms (eg DarkRoom) won't show you whats' going on unless you are carrying a light.

Room.java: Rooms have a description and a list of exits (north, south, east, west, up, down, though others are possible). Rooms also have the following virtual methods:

Each room contains a list of Items found in the room; Items are portable and can be TAKEn or DROPped.

RoomMaker.java: creates the map of rooms. Technically this is called a directed graph. (This isn't "just" a directed graph, or digraph; the different links have names.)

Then there are the special rooms:

DarkRoom: if you don't have a light, only the way out is shown. No Items in the room are visible. (And you cannot TAKE them if you guess their name)

DustyRoom: if you SWEEP, you find a WAND

LockedRoom: One exit is locked, and you can only go that way if you have a key in your inventory. No "unlock" action is needed.

SerpentRoom: contains a giant but harmless serpent. He does block your way back, though.

ValveRoom: You can't go DOWN unless you first TURN VALVE

ZooRoom: contains a sad zookeeper looking for a very small serpent

Downcasting

The player carries around a list of Items. How do we check this list to see if one of the Items is a Light, and, if so, is actually Lighted? There is no method in the Item class for asking if it is a Light, let alone the light's status. Polymorphism, in other words, is no help to us. Polymorphism applies only to methods that are part of the base class (and then redefined by the derived class), not to methods (like Light.is_lighted()) that apply to the

The process of checking an object belonging to a base class (Item) to see if it is in fact a member of a derived class, and, if so, viewing it as such, is called downcasting.

The Light class has two static methods that do downcasting; here is getLight() that returns the first lighted Light object on a List<Item>:
    public static Light getLight(List<Item> inv) {
        int i = 0;
        for (Item itm : inv) {
            if (itm instanceof Light) {
                Light lt = (Light) itm;         // note (cast)
                if (lt.isOn()) return lt;
            }
        }
        return null;    // get here if not found
    }
Note the "if (itm instanceof Light)" which is a test for whether an Item is a Light, and "Light lt = (Light) itm", which is the actual conversion. If itm is not a Light, then the case would throw an exception.

Shapesworld

Suppose we have the Shape class from earlier, and we want to go through shapeList to find each Rectangle and modify it by rounding its corners with the Rectangle-specific method setRoundCorners(). We can do the following:

    for (Shape s : ShapeList) {
        if (s instanceof Rectangle) {
            Rectangle r = (Rectangle) s;
            r.setRoundCorners();
    }

If all we wanted to do was draw the rectangles, we could use this; we don't have to downcast as we're not actually calling any method that is not present for the Shape class:

    for (Shape s : ShapeList) {
        if (s instanceof Rectangle) s.draw();
    }

Commands and double dispatch

I want "turn valve" to work only in the Valveroom. However, it is then important that "turn" be at least recognized everywhere, or else someone might erroneously conclude that "turn" isn't a game command at all. In other rooms, the response is "Hmm.. I don't seem to know how to turn here!" which is a clue that "turn" might work somewhere else.  How do I do this?

In the Room class, there is a catchall: if the verb word is legitimate (as determined by the CommandWords class), then the Room.response() is "Hmm ... I don't seem to know how to " + commandWord + "here!".

This means that each new verb must be entered into the CommandWords class.

It would also be possible to add to CommandWords a default response for each verb to be used in rooms that didn't "understand" that verb. In this case we might have a polymorphic verb.response() method; each verb would have its own class and would have its own response().

This is double dispatch: we want response() to depend on both the room and the verb, polymorphically. There are languages that allow this directly, but Java (and most OO languages, in fact) are a little more constrained. If we took that route, we would first try room.response(verb) and then, if that "failed", would try verb.response() (probably built into the default Room.response().