390 likes | 526 Views
Engr 691 Special Topics in Engineering Science Software Architecture Spring Semester 2004 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
E N D
Engr 691Special Topics in Engineering ScienceSoftware ArchitectureSpring Semester 2004Lecture 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)
Idealization of is-a Relationship A TextWindowis-aWindow Because TextWindow subclasses Window, all behavior of Window present in instances of TextWindow Therefore, variable declared as Window should be able to hold value of type TextWindow Unfortunately, practical programming language implementation issues complicate this idealized picture.
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 – variable 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 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; }
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; }
Polymorphic Variables(class Circle) class Circle extends Shape { public Circle (int ix, int iy, int ir) { super(ix, iy); radius = is; } public String describe() { return "circle with radius " + radius; } protected int radius; }
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
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 { ...}
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 ); } }
Polymorphic Variables(classSolitaireFramefrom Solitaire) private class SolitaireFrame extends Frame { ... public void paint(Graphics g) { for (int i = 0; i < 13; i++) allPiles[i].display(g); } }
Memory Allocation in Programming Languages • Static allocation • Stack-based allocation • Heap-based allocation
Memory Allocation Stack-based 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
Memory Allocation Heap-based 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 stack-based
Memory Allocation in Java • All object variables hold pointers to heap-allocated 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
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 Circle • Pointer variables hold addresses of objects – thus 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)
Copy versus Reference Semantics • Copy semantics: • assignment copies entire value of right side to left-side variable • the two values are independent; changes to one do not affect 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 • now two references to same value; if value is changed, it can be observed using either reference • examples: 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; }
Java Reference Semantics Example (continued) public class BoxTest { static public void main(String[] 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()); } } 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
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();
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
Example: Java Clones Make Box a Cloneable object • Implement interface Cloneable • Override method clone() (which returns type Object) • Make clone() public
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; }
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
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
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
Parameter Passing as Assignment (continued) 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
Equality Testing Primitive Types • How 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 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"
Equality Testing Object Identity (continued) 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) method to do bitwise comparisons, often redefined Continuing the Shape/Circle from the previous page if (x.equals(y)) System.out.println("equivalent") else System.out.println("not equivalent") Output is "equivalent"
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
Equality Testing Object Value Equality (cont.) 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
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");
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 • An argument or return value made more specialized is covariant • type replaced by descendant of original type • An argument made more general is contravariant • Both can destroy is-a relation, have tricky semantics • Most languages forbid both • Java and C++ forbid • Eiffel supports covariance
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
Storage Deallocation (continued) 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
Storage Deallocation (continued) 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