Session 15 Chapter 8 Understanding Inheritance Lab Exercise
Session 15 Chapter 8: Understanding Inheritance
Lab Exercise Solution • Recall that we needed to know if a mouse. Press occurred on a target. We tried to leverage the intersects method of Pin. Ball. Target interface by creating a temporary ball at the point of the mouse press: Ball temp. Ball = new Pin. Ball( e. get. X(), e. get. Y() ); for ( int j = 0; j < targets. size(); j++ ) { Pin. Ball. Target target=(Pin. Ball. Target) targets. element. At(j); if ( target. intersects( temp. Ball ) ) { start. Pressed = 1; element = target; } // end if } // end for
Question • If you had to pick one word that identifies the key concepts of this course so far, what would it be? Why?
My Answer • If you had to pick one word that identifies the key concepts of this course so far, what would it be? Why? – I think I might pick “Inheritance”
Inheritance What? A mechanism for reusing code in an existing class. A mechanism for organizing kinds of objects that have the same implementation. How? Create a class that extends another class. Why? Who wants to rewrite code? ? Reuse provides: • reliability through continual testing • shorter development time • ability to build frameworks (don’t call us. . . ) You can quickly build an application for demonstration purposes.
Exercise On Page 132, Budd describes how we might implement the class Set as a subclass of class Vector. A Set lets you add an element to it only if the Set doesn’t already contain that element. What messages should a set respond to?
Another Exercise On Page 132, Budd describes how we might implement the class Set as a subclass of class Vector. A Set lets you add an element to it only if the Set doesn’t already contain that element. – – – add. Element( Object ) remove. Element( Object ) contains( Object ) is. Empty() size() If ‘Set extends Vector’, which of these are already in existence? Which are correct? http: //java. sun. com/j 2 se/1. 4. 2/docs/api/java/util/Vector. html
Another Exercise On Page 132, Budd describes how we might implement the class Set as a subclass of class Vector. A Set lets you add an element to it only if the Set doesn’t already contain that element. – – – add. Element( Object ) remove. Element( Object ) contains( Object ) is. Empty() size() Write the add. Element method that we need to complete the Set class.
Another Exercise On Page 132, Budd describes how we might implement the class Set as a subclass of class Vector. A Set lets you add an element to it only if the Set doesn’t already contain that element. – – – add. Element( Object ) remove. Element( Object ) contains( Object ) is. Empty() size() If ‘Set extends Vector’ What other problem(s) do we have?
A possible solution import java. util. Vector; public class Set extends Vector { public Set() { super(); } public void add. Element( Object object ) { if ( !contains( object )) super. add. Element( object ); } public int index. Of( Object new. Element ) { System. out. println("Set. index. Of is not allowed. " ); return -1; } public Object element. At( int index ) { return null; } }
Inheritance In one sense, a subclass is an expansion of its superclass. • a subclass can add instance variables and methods In another sense, a subclass is a contraction of its superclass. • a subclass defines a subset of instances of its superclass In Java, all classes are subclasses, whether we say so or not. By default, any class that does not have an extends clause extends the class Object. Consider our Ball hierarchy: Ball, Movable. Ball, Bounded. Ball
public class Ball { private Rectangle location; private Color color; public Ball( int x, int y, int r ) {. . . } public void paint( Graphics g ) {. . . } public void set. Color( Color new. Color ) {. . . } protected Rectangle region() {. . . } protected Color color() {. . . } protected int radius() {. . . } protected int x() {. . . } protected int y() {. . . } protected void move. To( int x, int y ) {. . . } } public class Movable. Ball extends Ball { private double dx; private double dy; public Movable. Ball( int x, int y, int r ) {. . . } public void move() {. . . } public void set. Motion( double ndx, double ndy ) {. . . } protected double x. Motion() {. . . } protected double y. Motion() {. . . } } public class Bounded. Ball extends Movable. Ball { private Frame my. World; public Bounded. Ball( int x, int y, int r, Frame my. World ) {. . . } public void move() {. . . } }
Inheritance and Substitutability An object X is substitutable for an object Y if • we can use X any place we use Y and • the client code not know the difference. An example in practice, from the pinball games: • The target vector holds any Object. That’s how Java Vectors work. • We put Springs, Walls, Holes, . . . , into the vector. • When we retrieve objects from the vector, we treat them as Pin. Ball. Targets.
Inheritance and Substitutability Another example in practice, from the cannon games: • Our “fire” Button expects to be given an Action. Listener that watches for button events. • We create Fire. Button. Listener as an implementation of the Action. Listener interface. • We add a Fire. Button. Listener in place of an Action. Listener. What would the alternative be?
Substitutability The common feature in these cases — and the key to substitutability — is that the objects share a common interface. They respond to the same messages. Inheritance and interfaces are mechanisms for ensuring the common interface.
Substitutability So, why write our programs so that they use substitutable objects? • extendibility • flexibility • frameworks that implement a program’s control while allowing programmers to add new objects to the program later Of course, we can achieve these benefits without the use of inheritance and interfaces. But the compiler wouldn’t be able to help us enforce them!
Types of Inheritance • • • Specialization Specification Extension Combination Limitation Construction
Types of Inheritance Specialization • Essentially no new methods in the subclass. • Most subclass methods override inherited methods. • example: our Bounded. Ball class • common in frameworks
Types of Inheritance Specification • Superclass provides responsibility but no behavior. • Implement an interface or extend an abstract class. • example: our event listeners, pinball targets private class Mouse. Keeper extends Mouse. Adapter { private Pin. Ball. Target element; public void mouse. Pressed ( Mouse. Event e ) {. . . } public void mouse. Released( Mouse. Event e ) {. . . } }
Number - Abstract Class Example • Parent class for numeric wrapper classes: Integer, Long, Double, etc. • Subclasses must override abstract methods public abstract class Number { public abstract int. Value(); public abstract long. Value(); public abstract float. Value(); public abstract double. Value() public byte. Value() {return (byte) int. Value; } public short. Value() { return (short) int. Value(); } } // end Number
Types of Inheritance Extension • Subclass uses most or all inherited methods as-is. • Subclass adds new behavior and methods. • example: our Movable. Ball class
Types of Inheritance Combination • A class inherits from two or more classes. This is called multiple inheritance. • Some OOP languages provide it (C++). Some don’t (Java, Smalltalk). • Java does support combination through interfaces. • example: Budd’s Hole class Hole extends Ball implements Pin. Ball. Target { public Hole( int x, int y ) {. . . } public boolean intersects( Ball a. Ball ) {. . . } public void hit. By ( Ball a. Ball ) {. . . } }
Other Types of Inheritance Limitation • The subclass primarily has methods that override inherited methods. • to restrict a behavior (example: Square extends Rectangle) • to remove a behavior (example: Set extends Vector) • Limitation violates the principle of substitutability.
Other Types of Inheritance Construction • The subclass reuses a class because it provides needed functionality. . . • . . . but it is not necessarily true that an instance of the subclass is an instance of the superclass. • example: Java’s Stack class (ouch!) • Construction may violate the principle of substitutability. JUST DON’T DO IT.
An Exercise public class Set extends Vector. . . If limitation and construction are bad ideas, what should we do instead? ? ? Implement your own Set class that avoids the improper use of inheritance with limitation. Sets respond to these messages: • add. Element( Object ) • remove. Element( Object ) • contains( Object ) • is. Empty() • size()
Class Interaction Relationships between classes: 1. composition – A Ball contains a Color. – An instance of Color is a part of a Ball. 2. inheritance – A Movable. Ball is a kind of Ball. – A Cannon. World is a kind of Frame. 3. association – A Bounded. Ball interacts with the Frame that contains it.
An Exercise • Inheritance is only one way to reuse a class. Another is composition: having an instance of the class as an instance variable. • So, instead of extending Vector, a Set can have a Vector instance variable, to which the set adds only elements that aren’t already in the set.
Possible Solution for Set Class import java. util. Vector; public class Set { private Vector my. Stuff; public Set() { my. Stuff = new Vector(); } // Methods provided in a bit }
Possible Methods for Set Class public void add. Element( Object new. Element ) { if ( !( my. Stuff. contains( new. Element ) ) ) my. Stuff. add. Element( new. Element ); } public boolean remove. Element( Object new. Element ) { return my. Stuff. remove. Element( new. Element ); } public boolean contains( Object new. Element ) { return my. Stuff. contains( new. Element ); } public boolean is. Empty() { return my. Stuff. is. Empty(); } public int size() { return my. Stuff. size(); }
A driver class import Set; public class Set. Demo { public static void main( String[] args ) { Set arguments = new Set(); for (int i = 0; i < args. length; i++) arguments. add. Element( args[i] ); System. out. println( "Is the set empty? " + arguments. is. Empty() ); System. out. println( "Number of unique arguments: " +arguments. size() ); } }
Sample output $ javac Set. Demo. java $ java Set. Demo Is the set empty? true Number of unique arguments: 0 $ java Set. Demo a b a b a b Is the set empty? false Number of unique arguments: 2
Reconsidering the Design of Cannon. World • Why limit our shelves to shooting cannon balls? !!!
Reconsidering the Design of Cannon. World But does it have to shoot only cannon balls? Of course not! All the Cannon. World needs is an object that can. . . • move under its own control • paint itself to the screen • tell the Cannon. World where it lies relative to the floor and the target
Currently, Cannon. World is hardwired to a Cannon. Ball: public class Cannon. World extends Frame {. . . private Cannon. Ball cannon. Ball; . . . public Cannon. World() {. . . cannon. Ball = null; . . . }. . . protected void draw. Cannon. Ball(Graphics g) { if ( cannon. Ball != null ). . . cannon. Ball. move(); cannon. Ball. paint( g ); . . . cannon. Ball. y(). . . cannon. Ball. x(). . . cannon. Ball = null; . . . }. . . private class Fire. Button. Listener. . . { public void action. Performed(. . . ) {. . . cannon. Ball = new Cannon. Ball (. . . ); . . . } }. . . }
The Current State of Affairs The Cannon. World class is hardwired to the Cannon. Ball class in 3 ways: • It sends messages that are defined in the Cannon. Ball class. • It declares an instance variable of type Cannon. Ball. • It calls new with a request for an instance of class Cannon. Ball.
The Problem with the Current State of Affairs • It sends messages that are defined in the Cannon. Ball class. – This limits Cannon. World to working with the decisions made when implementing Cannon. Ball. If the Cannon. Ball class was designed well, that shouldn’t be a problem. But. . . • It declares an instance variable of type Cannon. Ball. – This limits Cannon. World to working with an instance of class Cannon. Ball, or an instance of a subclass of Cannon. Ball. But. . . • It calls new with a request for an instance of class Cannon. Ball. – This limits Cannon. World to working only with instances of class Cannon. Ball — no subclasses allowed!
Reconsidering the Design of Cannon. World Notice that the Cannon. World doesn’t care. . . • that its cannonball is affected by gravity. . . If the object moves under its own control, then gravity is a feature of the object flying through the air! • what the x- and y-coordinates of its cannonball are. It cares whether the ball has hit the floor and the target. The messages sent by Cannon. World to its cannon. Ball are an unfortunate remnant of an implementation decision in the Cannon. Ball class. Because this is true, we can generalize Cannon. World so that it works with more than just Cannon. Balls.
Step 1: Make a Projectile Interface Write an interface named Projectile that specifies objects which can. . . • move() according to a trajectory they know, • paint() themselves to a Graphics context, and • respond to boolean requests for their location relative to a Point.
import java. awt. Graphics; import java. awt. Point; public interface Projectile { public void move (); public void paint ( Graphics g ); public boolean north. Of( Point p ); public boolean south. Of( Point p ); public boolean east. Of ( Point p ); public boolean west. Of ( Point p ); } (Projectile. java)
Step 1: Make a Projectile Interface This interface defines the essential responsibilities of the objects that a Cannon. World shoots. Notice that this interface requires the object to respond to queries about its relative location, rather than opening up its instance variables for outside inspection. The only methods to look at an object’s instance variables should be its own instance methods.
Step 2: Make Cannon. Ball Implement the Projectile Interface We would like our Cannon. World to fire an instance of any class that implements the Projectile interface, but that mean that Cannon. Balls must be Projectiles. So let’s refactor the Cannon. Ball class so that it also implements the Projectile interface: (Cannon. Ball. java)
Step 3: Make Cannon. World Fire Any Projectile If we write our Cannon. World program to use the Projectile interface, then we are able to “plug in” new projectiles relatively easily. Programming to an abstract interface makes your programs easier to reuse and less likely to depend on implementation detail.
Step 3: Make Cannon. World Fire Any Projectile In order for a Cannon. World to shoot Projectiles like our new Cannon. Balls from its cannon, we need to modify Cannon. World so that it uses a Projectile instead of a Cannon. Ball. So: Modify all references to the Cannon. Ball in Cannon. World to be references to a Projectile.
Step 3: Make Cannon. World Fire Any Projectile This causes something of a problem, because we cannot create an instance of an interface. So: Move the creation of the Cannon. Ball into an abstract protected helper method. The code that creates the Cannon. Ball should call this method instead of calling new directly. This requires that the class be abstract, too.
Step 3: Make Cannon. World Fire Any Projectile These changes are actually pretty straightforward. . . • Change the instance variable to be a Projectile. • Leave all null assignments and move/paint messages the same. • Change requests for the Cannon. Ball’s instance variables (and the calculations on those values!) into requests to a Projectile. • Replace the creation of the Cannon. Ball with a call to the abstract method that creates instances. • (Projectile. World)
We have just converted our Cannon. World program into a framework
The Result: A Projectile. World Framework Projectile. World is not just one program now. It is either zero programs or as many programs as you can imagine. • We can no longer run the Projectile. World program by itself. In this way, it is not a program at all. • But our new Projectile. World allows us to build a working program that fires the projectile of our choice in just two steps.
Using the Projectile. World Framework To implement a working Projectile. World, a client programmer need only: • Write a class that implements the Projectile interface. • Write a subclass of Projectile. World that implements the make. Projectile(. . . ) method to create an instance of the new Projectile class.
Step 4: Make a Cannon. World Class Hey! We already have the Projectile of our choice: the Cannon. Ball. So we only have to do the second step. In order for a P r o j e c t i l e W o r l d to shoot Cannon. Balls from its cannon, we need to create a Cannon. World that uses a Cannon. Ball instead of a Projectile. So: (Revised. Cannon. World. java)
The Projectile. World Framework We have built a Projectile. World framework: two classes that define an infinite number of programs in terms of the abstract behaviors of their collaborators. The “user” of this framework is a programmer who instantiates the abstract behaviors in concrete classes. But these concrete classes inherit the bulk of the program’s behavior from the classes in the framework.
The Projectile. World Framework The Projectile. World framework is the same in principle as the Java AWT. • The Projectile. World class is a lot like the Frame class, which allows us to build functional windows with relative ease. • The Projectile interface is a lot like the listener interfaces that allow us to respond to user events with relative ease. • The control of the application lies in Projectile. World. The programmer uses the framework classes with a “don’t call us; we’ll call you” mentality.
The Projectile. World Framework The make. Projectile() method is a factory method. It creates an object for the program, so that the program does not have to use the new command directly. (This is a common idea in frameworks, because we don’t want our abstract class to commit to a class for its instance vars!)
- Slides: 52