Chapter 8: Understanding Inheritance
Download
Report
Transcript Chapter 8: Understanding Inheritance
Session 15
Chapter 8: Understanding Inheritance
Lab Exercise Solution
• Recall that we needed to know if a mousePress
occurred on a target. We tried to leverage the
intersects method of PinBallTarget interface by
creating a temporary ball at the point of the mouse
press:
Ball tempBall = new PinBall( e.getX(), e.getY() );
for ( int j = 0; j < targets.size(); j++ ) {
PinBallTarget target=(PinBallTarget) targets.elementAt(j);
if ( target.intersects( tempBall ) ) {
startPressed = 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.
–
–
–
–
–
addElement( Object )
removeElement( Object )
contains( Object )
isEmpty()
size()
If ‘Set extends Vector’, which of these are
already in existence? Which are correct?
http://java.sun.com/j2se/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.
–
–
–
–
–
addElement( Object )
removeElement( Object )
contains( Object )
isEmpty()
size()
Write the addElement 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.
–
–
–
–
–
addElement( Object )
removeElement( Object )
contains( Object )
isEmpty()
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 addElement( Object object ) {
if ( !contains( object ))
super.addElement( object );
}
public int indexOf( Object newElement ) {
System.out.println("Set.indexOf is not allowed." );
return -1;
}
public Object elementAt( 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, MovableBall,
BoundedBall
public class Ball {
private Rectangle location;
private Color color;
public Ball( int x, int y, int r ) { ... }
public void paint( Graphics g ) { ... }
public void setColor( Color newColor ) { ... }
protected Rectangle region() { ... }
protected Color color() { ... }
protected int radius() { ... }
protected int x() { ... }
protected int y() { ... }
protected void moveTo( int x, int y ) { ... }
}
public class MovableBall extends Ball {
private double dx;
private double dy;
public MovableBall( int x, int y, int r ) { ... }
public void move() { ... }
public void setMotion( double ndx, double ndy ) { ... }
protected double xMotion() { ... }
protected double yMotion() { ... }
}
public class BoundedBall extends MovableBall {
private Frame myWorld;
public BoundedBall( int x, int y, int r, Frame myWorld ) { ... }
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 PinBallTargets.
Inheritance and Substitutability
Another example in practice, from the cannon
games:
• Our “fire” Button expects to be given an
ActionListener that watches for button events.
• We create FireButtonListener as an
implementation of the ActionListener interface.
• We add a FireButtonListener in place of an
ActionListener.
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 BoundedBall 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 MouseKeeper extends MouseAdapter {
private PinBallTarget element;
public void mousePressed ( MouseEvent e ) { ... }
public void mouseReleased( MouseEvent 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 intValue();
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue()
public byte byteValue() {return (byte) intValue;}
public short shortValue() {
return (short) intValue();
}
} // end Number
Types of Inheritance
Extension
• Subclass uses most or all inherited methods
as-is.
• Subclass adds new behavior and methods.
• example: our MovableBall 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
class Hole extends Ball implements PinBallTarget {
public Hole( int x, int y ) { ... }
public boolean intersects( Ball aBall ) { ... }
public void hitBy ( Ball aBall ) { ... }
}
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:
• addElement( Object )
• removeElement( Object )
• contains( Object )
• isEmpty()
• 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 MovableBall is a kind of Ball.
– A CannonWorld is a kind of Frame.
3. association
– A BoundedBall 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 myStuff;
public Set() {
myStuff = new Vector();
}
// Methods provided in a bit
}
Possible Methods for Set Class
public void addElement( Object newElement ) {
if ( !( myStuff.contains( newElement ) ) )
myStuff.addElement( newElement );
}
public boolean removeElement( Object newElement ) {
return myStuff.removeElement( newElement );
}
public boolean contains( Object newElement ) {
return myStuff.contains( newElement );
}
public boolean isEmpty() {
return myStuff.isEmpty();
}
public int size() {
return myStuff.size();
}
A driver class
import Set;
public class SetDemo {
public static void main( String[] args ) {
Set arguments = new Set();
for (int i = 0; i < args.length; i++)
arguments.addElement( args[i] );
System.out.println( "Is the set empty? " +
arguments.isEmpty() );
System.out.println( "Number of unique
arguments: " +arguments.size() );
}
}
Sample output
$ javac SetDemo.java
$ java SetDemo
Is the set empty? true
Number of unique arguments: 0
$ java SetDemo a b a b a b a b a b
Is the set empty? false
Number of unique arguments: 2
Reconsidering the Design of
CannonWorld
• Why limit our shelves to shooting cannon
balls?!!!
Reconsidering the Design of
CannonWorld
But does it have to shoot only cannon balls?
Of course not! All the CannonWorld needs
is an object that can...
• move under its own control
• paint itself to the screen
• tell the CannonWorld where it lies relative
to the floor and the target
Currently, CannonWorld is
hardwired to a CannonBall:
public class CannonWorld extends Frame { ...
private CannonBall cannonBall;
...
public CannonWorld() { ...
cannonBall = null;
...}
...
protected void drawCannonBall(Graphics g) {
if ( cannonBall != null )
...
cannonBall.move();
cannonBall.paint( g );
... cannonBall.y() ...
... cannonBall.x() ...
cannonBall = null;
...}
...
private class FireButtonListener ...{
public void actionPerformed( ... ) { ...
cannonBall = new CannonBall (...);
...}
}
...}
The Current State of Affairs
The CannonWorld class is
hardwired to the
CannonBall class in 3
ways:
• It sends messages that
are defined in the
CannonBall class.
• It declares an instance
variable of type
CannonBall.
• It calls new with a
request for an instance
of class CannonBall.
The Problem with the Current
State of Affairs
• It sends messages that are defined in the
CannonBall class.
– This limits CannonWorld to working with the decisions made
when implementing CannonBall. If the CannonBall class was
designed well, that shouldn’t be a problem. But...
• It declares an instance variable of type CannonBall.
– This limits CannonWorld to working with an instance of class
CannonBall, or an instance of a subclass of CannonBall. But...
• It calls new with a request for an instance of class
CannonBall.
– This limits CannonWorld to working only with instances of class
CannonBall — no subclasses allowed!
Reconsidering the Design of
CannonWorld
Notice that the CannonWorld 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 CannonWorld to its cannonBall
are an unfortunate remnant of an implementation
decision in the CannonBall class.
Because this is true, we can generalize CannonWorld so
that it works with more than just CannonBalls.
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 northOf( Point p );
public boolean southOf( Point p );
public boolean eastOf ( Point p );
public boolean westOf ( Point p );
}
(Projectile.java)
Step 1: Make a Projectile Interface
This interface defines the essential responsibilities
of the objects that a CannonWorld 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 CannonBall Implement
the Projectile Interface
We would like our CannonWorld to fire an
instance of any class that implements the
Projectile interface, but that mean that
CannonBalls must be Projectiles. So let’s
refactor the CannonBall class so that it
also implements the Projectile interface:
(CannonBall.java)
Step 3: Make CannonWorld Fire
Any Projectile
If we write our CannonWorld 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 CannonWorld Fire
Any Projectile
In order for a CannonWorld to shoot
Projectiles like our new CannonBalls from
its cannon, we need to modify
CannonWorld so that it uses a Projectile
instead of a CannonBall. So:
Modify all references to the CannonBall
in CannonWorld to be references to a
Projectile.
Step 3: Make CannonWorld Fire
Any Projectile
This causes something of a problem, because we
cannot create an instance of an interface. So:
Move the creation of the CannonBall into an
abstract protected helper method. The code
that creates the CannonBall should call this
method instead of calling new directly. This
requires that the class be abstract, too.
Step 3: Make CannonWorld 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 CannonBall’s instance
variables (and the calculations on those values!)
into requests to a Projectile.
• Replace the creation of the CannonBall with a
call to the abstract method that creates
instances.
• (ProjectileWorld)
We have just converted our CannonWorld program into
a framework
The Result:
A ProjectileWorld Framework
ProjectileWorld 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 ProjectileWorld
program by itself. In this way, it is not a
program at all.
• But our new ProjectileWorld allows us to
build a working program that fires the
projectile of our choice in just two steps.
Using the ProjectileWorld
Framework
To implement a working ProjectileWorld, a
client programmer need only:
• Write a class that implements the
Projectile interface.
• Write a subclass of ProjectileWorld that
implements the makeProjectile(...) method
to create an instance of the new Projectile
class.
Step 4: Make a CannonWorld
Class
Hey! We already have the Projectile of our
choice: the CannonBall. 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 CannonBalls from its cannon, we
need to create a CannonWorld that uses a
CannonBall instead of a Projectile. So:
(RevisedCannonWorld.java)
The ProjectileWorld Framework
We have built a ProjectileWorld 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 ProjectileWorld Framework
The ProjectileWorld framework is the same in
principle as the Java AWT.
• The ProjectileWorld 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
ProjectileWorld. The programmer uses the
framework classes with a “don’t call us; we’ll call
you” mentality.
The ProjectileWorld Framework
The makeProjectile() 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!)