Transcript slides
Csci 490 / Engr 596
Special Topics / Special Projects
Software Design and Scala Programming
Spring Semester 2010
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
Updated Edition
(Addison-Wesley, 2000)
Created: 14 August 2004 Revised: 11 March 2010
Idealization of is-a Relationship
A TextWindow is-a Window
TextWindow subclasses Window, ideally,
All behavior of Window present in instances of
TextWindow
Variable declared as Window should be able to
hold value of type TextWindow
Unfortunately, practical programming language
implementation issues complicate this idealized
picture.
1
Impact of Inheritance on
Other Language Features
Support for inheritance and principle of substitutability
impacts most aspects of a programming language
Polymorphic variables needed
Polymorphic variable is declared as one type but actually holds
value of subtype
Polymorphic variables better if objects allocated on heap
rather than stack – storage requirements vary among
subtypes
Heap allocation
Makes reference (pointer) semantics more natural than copy
semantics for assignment and parameter passing
copy address of object rather than value
Makes reference semantics more natural for object identity testing –
compare addresses – separate operator for object value equality
Requires storage management – reference semantics makes manual
2
allocation difficult – garbage collection encouraged
Polymorphic Variables
Class Shape
class Shape
{ public Shape (int ix, int iy)
{ x = ix;
y = iy;
}
public String describe()
{ return "unknown shape"; }
protected int x;
protected int y;
}
3
Polymorphic Variables
Class Square
class Square extends Shape
{ public Square(int ix, int iy, int is)
{
super(ix, iy);
side = is;
}
public String describe()
{
return "square with side " + side;
}
protected int side;
}
4
Polymorphic Variables
Class Circle
class Circle extends Shape
{ public Circle (int ix, int iy, int ir)
{
super(ix, iy);
radius = ir;
}
public String describe()
{
return "circle with radius " + radius;
}
protected int radius;
}
5
Polymorphic Variables
Class ShapeTest
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
6
Polymorphic Variables
Class CardPile from Solitaire
public class CardPile { ... }
class SuitPile extends CardPile { ... }
class DeckPile extends CardPile { ... }
class DiscardPile extends CardPile { ... }
class TablePile extends CardPile { ...}
7
Polymorphic Variables
Class Solitaire from Solitaire Example
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);
}
}
8
Polymorphic Variables
Class SolitaireFrame from Solitaire
private class SolitaireFrame extends Frame
{ ...
public void paint(Graphics g)
{
for (int i = 0; i < 13; i++)
allPiles[i].display(g);
}
}
9
Memory Allocation
in Programming Languages
Static allocation
Stack-based allocation
Heap-based allocation
10
Stack-based Memory Allocation
Memory allocated dynamically on runtime stack
Memory allocation/release tied to procedure entry/exit
Space requirement determined at compile time based
on static types
Advantage: efficient
all local variables allocated/deallocated as a block
(activation record)
Disadvantage: polymorphic variable size not known at
compile time
objects stored may vary during execution
11
Heap-based Memory Allocation
Memory allocated dynamically from free memory area
Memory allocation/release not tied to procedure
entry/exit
Space requirement determined at run-time based using
dynamic considerations – size known when allocated
Allocated objects accessed by indirection through a
pointer (reference in Java)
Advantage: supports polymorphic variables
values can be pointers to object on heap
Disadvantage: considered less efficient than stackbased
12
Memory Allocation in Java
All object variables hold pointers to heapallocated objects – fixed size on stack, differing
sizes on heap
Variable of type Shape holds address of object on heap
Polymorphic variables thus easy to implement
Assignment of Square instance to Shape variable means
new address stored
Primitive type variables hold values, copy on
assignment, not polymorphic
13
Memory Allocation in C++
Variables stored on stack
Enough space allocated to hold instance of actual declared type
Variable of type Shape holds actual object
"Ordinary" variables are not polymorphic
Assignment of Square instance to Shape variable means object
copied with extra field sliced off – no longer Square
Pointer variables hold addresses of objects
Support polymorphism
Assignment of Square pointer to Shape pointer variable means
new address stored
Objects may be allocated on heap (or on stack or
statically)
Care must be taken with pointers to deallocated objects on stack
(or in heap memory)
14
Copy versus Reference Semantics
Copy semantics
Assignment copies entire value of right side to left-side variable
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 left-side variable to refer to right-side value
Two references same value; if value is changed, it can be
observed using either reference
Examples: Java assignments to object variables
C++ assignments to pointer variables.
15
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;
}
16
Java Reference Semantics Example
Example (cont.)
public class BoxTest
{ static public void main(String[] args)
{ Box x = new Box();
x.setValue(7);
// sets value of x
Box y = x;
y.setValue(11);
// assign y the same value as y
// change value of y
System.out.println("contents of x " + x.getValue());
System.out.println("contents of y " + y.getValue());
}
}
After y = x, both x and y in BoxTest refer to the same object
Call y.setValue(11) thus changes object referred to by both x and y
Message getValue() thus returns same value (11) for both x and y
17
Creating Copies in Java
If need copy, explicitly create it
Box y = new Box(x.getValue());
If commonly need copy, provide copy-creating method
public class Box
{ ...
public Box copy()
{ Box b = new Box();
b.setValue(getValue());
return b;
} // return (new Box()).setValue(getValue())
...
}
and use method when needed
Box y = x.copy();
18
Creating Copies in Java
Copy constructors are sometimes useful
public class Box
{ ...
public Box(Box x)
{
value = x.getValue());
...
}
}
Base class Object has protected method clone()
that creates bitwise copy of receiver
Interface Cloneable denotes objects that can be
cloned – no methods in interface, just tag
Objects needed by some API methods
19
Example
Java Clones
Make Box a Cloneable object
Implement interface Cloneable
Override method clone() (which returns
type Object )
Make clone() public
20
Example
Java Clones (cont.)
public class Box implements Cloneable
{ public Box()
{
value = 0;
}
public void setValue(int v)
{
value = v;
}
public int getValue()
{
return value; }
public Object clone()
{
return (new Box()).setValue(getValue()); }
private int value;
}
21
Example
Java Clones (cont.)
public class BoxTest
{ static public void main(String[] args)
{
Box x = new Box();
x.setValue(7); // sets value of x
Box y = (Box) x.clone(); // assign copy of x to y
y.setValue(11); // change value of y
System.out.println("contents of x " + x.getValue());
System.out.println("contents of y " + y.getValue());
}
}
Values 7 and 11, respectively, would be printed by BoxTest
22
Shallow versus Deep Copying
Suppose values being held by Box objects are
themselves objects of type Shape (instead of int)
Box's clone() would not copy Shape object
Clones would both refer to same Shape object
clone() creates a shallow copy
If internal Shape object also copied, then it is a
deep copy
Box's method clone() could call Shape's clone()
operation
Decide whether shallow or deep copy is needed for
application
23
Parameter Passing as Assignment
Parameter-passing is assignment from argument
to parameter
Java primitive values are passed by value from
argument to parameter – copy semantics
Modification of parameter just local, no effect on argument
Java object variables are passed by reference from
argument to parameter – reference semantics
Note
Value of reference is copied from argument to parameter
Modification of parameter's internal state is change to argument
24
Parameter Passing as Assignment
Example
public class BoxTest
{ static public void main (String[] args)
{ Box x = new Box();
x.setValue(7); // sets value of x
sneaky(x);
System.out.println("contents of x " + x.getValue());
}
static void sneaky(Box y)
{
y.setValue(11);
}
}
Value 11 would be printed by BoxTest
25
Equality Testing
Primitive Types
How to test whether two values of 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 wellaccepted conversion between
Java: numeric types converted and compared, but
otherwise mismatched types means inequality
26
Equality Testing
Object Identity
Some languages 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"
27
Equality Testing
Object Identity (cont.)
Objects x and y physically distinct but same
value internally
Java type checker
Disallows comparison of unrelated object types with ==
Can compare if one an ancestor of other
eg.
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
28
Equality Testing
Object Value Equality
Java Object class has equals(Object) that
checks for object identity by default
eg. Circle x = new Circle(10, 10, 5);
Shape y = new Square(10, 10, 5);
Shape z = new Circle(10, 10, 5);
…
if (x.equals(y))
System.out.println("equivalent");
else
System.out.println("not equivalent") ;
Output is “not equivalent"
29
Equality Testing
Object Value Equality (cont.)
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
30
Equality Testing
Object Value Equality (cont.)
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
31
Equality Testing
Object Value Equality (cont.)
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");
32
Equality Testing
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) { ... }
}
33
Covariance and Contravariance
Covariant
An argument or return value made more specialized
Type replaced by descendant of original type
Contravariant
An argument made more general
Both can destroy is-a relation, have tricky
semantics
Most languages forbid both
Java and C++ forbid
Eiffel supports covariance
34
Storage Deallocation
Polymorphic variables lead naturally to
heap-based allocation
Heap-based allocation requires a storage
deallocation mechanism
Two approaches:
Explicit deallocation by programmer
Implicit deallocation by runtime system
35
Storage Deallocation
Explicit Deallocation by Programmer
Programmer must return unneeded memory to
system
Examples: C++ delete, Pascal dispose
Advantage: efficiency
Disadvantages
Attempted use of memory not yet allocated or already
freed
Multiple freeing of memory
Freeing of memory that is still needed
Memory leak – allocated memory is never released
36
Storage Deallocation
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
37
Acknowledgement
This work was supported by a grant from
Acxiom Corporation titled “The Acxiom
Laboratory for Software Architecture and
Component Engineering (ALSACE).”
38