CSci 581: Obj.-Oriented Design & Programming
Spring Semester 1999
Lecture Notes


Implications of Inheritance (Budd's UOOPJ, Ch. 11)

This is a set of "slides" to accompany chapter 11 of Timothy Budd's textbook Understanding Object-Oriented Programming with Java (Addison-Wesley, 1998).


Idealization of is-a Relationship

Unfortunately, practical programming language implementation issues complicate this idealized picture.


Impact of Inheritance on Other Language Features

Support for inheritance and the principle of substitutability impacts most aspects of a programming language:


Polymorphic Variables

class Shape
{   public Shape (int ix, int iy) { x = ix; y = iy; }
    public String describe() { return "unknown shape"; }

    protected int x;
    protected int y;
}

class Square extends Shape
{   public Square(int ix, int iy, int is) { super(ix, iy); side = is; }
    public describe() { return "square with side " + side;}

    protected int side;
}

class Circle extends Shape
{   public Circle(int ix, int iy, int ir) { super(ix, iy); radius = is; }
    public describe() { return "circle with radius " + radius;}

    protected int radius;
}

class ShapeTest
{   public static void main(String[] args)
    {   Shape form = new Circle(10, 10, 5);
	System.out.println("form is " + form.describe(); 
    }
}


Output of ShapeTest: form is circle with radius 5


Polymorphic Variables (from Solitaire)

public class CardPile { ... }

class SuitPile extends CardPile { ... }

class DeckPile extends CardPile { ... }

class DiscardPile extends CardPile { ... }

class TablePile extends CardPile { ...}

public class Solitaire 
{   ...
    static public CardPile allPiles [ ];
    ...
    public void init () 
    {       // first allocate the arrays
        allPiles = new CardPile[13];
	...
        allPiles[0] = deckPile = new DeckPile(335, 30);
        allPiles[1] = discardPile = new DiscardPile(268, 30);
        for (int i = 0; i < 4; i++)
            allPiles[2+i] = suitPile[i] =
                new SuitPile(15 + (Card.width+10) * i, 30);
        for (int i = 0; i < 7; i++)
            allPiles[6+i] = tableau[i] =
                new TablePile(15 + (Card.width+5) * i, Card.height + 35, i+1); 
    }

    private class SolitaireFrame extends Frame 
    {   ...
        public void paint(Graphics g) 
        {   for (int i = 0; i < 13; i++) 
                allPiles[i].display(g);
        }
    }


Memory Allocation -- Stack and Heap Based

Generally, programming languages use two different techniques for allocation of memory:

Stack-based allocation:
memory allocated on the runtime stack

Heap-based allocation:
memory allocated in free memory area


Memory Allocation in Java and C++

Java

C++


Copy versus Reference Semantics

Copy semantics:
Assignment copies the entire value of right side to the left side variable.
The two values are independent; changes to one do not affect the other

Examples: Java assignments to primitive variables, C++ assignments to non-pointer variables

Reference (pointer) semantics:
Assignment changes the left side variable to refer to the right side value.
There are now two references to the exact same value; if the value is changed, it can be observed using either reference

Example: Java assignments to object variables, C++ assignments to pointer variables.


Java Reference Semantics Example

public class Box 
{   public Box() { value = 0; }
    public void setValue(int v) { value = v; }
    public int getValue() { return value; }

    private int value;
}

public class BoxTest
{   static public void main(Sring[] args)
    {   Box x = new Box();
	x.setValue(7);   // sets value of x

	Box y = x;       // assign y the same value as y
	y.setValue(11);  // change value of y

	System.out.println("contents of x " + x.getValue());
	System.out.println("contents of y " + y.getValue());
    }
}


Creating Copies in Java


Example: Java Clones

Make Box a Cloneable object


Shallow versus Deep Copying

Suppose that the values being held by the Box objects are themselves objects of type Shape


Parameter Passing as Assignment


Equality Testing -- Primitive Types

How do we test whether two values of the same primitive type are equal?

Test whether their values are identical -- i.e., same bits

Java: x == y

What about equality of values of different primitive types?

In general, will not pass type checker unless well-accepted conversion between

Java: numeric types converted and compared, but otherwise mismatched types means inequality


Equality Testing -- Object Identity

Some languages will compare the values; others, compare pointers (references)

Java uses pointer semantics -- i.e., tests object identity

Java == tests object identity
Integer x = new Integer(7);
Integer y = new Integer(3 + 4);
if (x == y)
    System.out.println("equivalent")
else
    System.out.println("not equivalent")

Output is "not equivalent"

Objects x and y physically distinct -- but same value internally

Java type checker disallows comparison of unrelated object types with ==

But can compare if one an ancestor of other
Circle x = new Circle(10, 10, 5);
Shape  y = new Square(10, 10, 5);
Shape  z = new Circle(10, 10, 5);

Above x == y and x == z pass type checking, but neither returns true

null is of type Object; can be compared for equality with any object


Equality Testing -- Object Value Equality

Java Object class has equals(Object) methods to do bitwise comparisons, often redefined

if (x.equals(y))
    System.out.println("equivalent")
else
    System.out.println("not equivalent")

Output is "equivalent"

Can override equals() to get more appropriate definition

class Circle extends Shape
{   ...
    public boolean equals(Object arg)
    {   return arg instanceof Circle &&
               radius == ((Circle)arg).radius); 
    }  // more compact above than textbook example
}

Above c.equals(d) iff c and d are both Circles with same radius regardless of location

Should override equals() if object contains other objects

But be careful with asymmetric equality comparisons

Suppose override equals() in Shape

class Shape
{   ...
    public boolean equals(Object arg)
    {   if (arg instanceof Shape)
        {   Shape s = (Shape)arg;
            return x == s.x && y = s.y ;
        }
        else
            return false;
    }
}

But not in subclass Square

Now consider

Square s = new Square(10,10,5);
Circle c = new Circle(10,10,5);
if (s.equals(c))  // true, uses Shape method
    System.out.println("square equal to circle");
if (c.equals(s)) // false, uses Circle method
    System.out.println("circle equal to square");


Changing Method Arguments

For equality testing, it might useful to change types of method arguments

class Shape 
{   ...
    public boolean equals (Shape s) { return false; }
}

class Circle extends Shape 
{   ...
    public boolean equals (Circle c) { ... }
}

class Square extends Shape 
{   ...
    public boolean equals (Square sq) { ... }
}


Covariance and Contravariance


Storage Deallocation

Polymorphic variables lead naturally to heap-based allocation

Heap-based allocation requires a storage deallocation mechanism

Two approaches were discussed previously

Explicit deallocation by programmer
-- programmer must return unneeded memory to system

Examples: C++ delete, Pascal dispose

Advantage: efficiency

Disadvantages:

Implicit deallocation by runtime system
-- system detects when data unneeded, automatically recovers memory
-- garbage collection

Examples: Java, Smalltalk, Perl

Advantage: safety and convenience

Disadvantage: relative inefficiency / loss of programmer control


UP to CSCI 581 Lecture Notes root document?


Copyright © 1999, H. Conrad Cunningham
Last modified: Wed Feb 2 21:07:31 2000