Download Using Inheritance

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Using Inheritance
Inheritance review
we have seen all of the mechanisms for creating
subclasses:
adding new methods
replacing methods from the parent by overriding
refining methods from the parent by overriding
now let's see what we can do with inheritance in our
programs
java.io.* example
various implementations for
different sources of data
subclasses mainly differ in
construction
Java's input/output classes use inheritance and composition
to provide a wide range of I/O facilities to programmers
composite classes, which
both inherit from and contain
an InputStream
reuses different data sources
without duplication of code
adds new functionality like
filtering and buffering
suitable for further
subclassing
Principle of substitutability
A derived class inherits all of the (public) attributes of it's
parent. Therefore, an instance of the derived class can be
substituted for an instance of the parent class.
We have already seen a special case of this with interfaces:
the Stack interface provides a common ancestor for SimpleStack
and BetterStack
client code is written in terms of the parent, Stack, because all
implementations inherit the attributes of Stack, and so are
substitutable.
A variable which refers to objects of different classes is
called a polymorphic variable.
java.lang.Object.toString()
in Java, all classes have java.lang.Object as a
common ancestor
thus all objects have a toString() method, which returns
a String that represents the object
if toString() is not overriden, it uses Object's
implementation, which returns the object's reference (a hex
value)
Methods like System.out.print( Object o ) use a
polymorphic variable (“o” in this case) to support many
classes with just one piece of code
Polymorphism
poly ~ many, morph ~ form
polymorphic variable – defined as one type, but may contain values
of other (sub)types.
overloading: a method name has many forms
parameteric overloading – different forms for different parameter
types
weak overloading – different forms for different classes
overriding: a method has different forms in derived classes
there are several kinds of polymorphism in modern OO
languages like Java:
a more robust version of weak overloading
Example: Polymorphism with Object
Object currentObject;
// our polymorphic variable
// create some objects
Integer
x = new Integer( 42 );
SimpleStack s = new SimpleStack();
IntArray
a = new IntArray( 10 );
// ...and somewhere to collect the strings
String myObjectStrings = “”;
currentObject = x; // currentObject now refers to an Integer
myObjectStrings += “ “ + currentObject.toString() + “\n”;
currentObject = s; // now it refers to a SimpleStack
myObjectStrings += “ “ + currentObject.toString() + “\n”;
currentObject = a; // now it refers to an IntArray
myObjectStrings += “ “ + currentObject.toString() + “\n”;
A tricky example: spot the polymorphism
public class Rectangle {
public Rectangle( int height, int width ) { ... }
public void draw( double scale ) { ... }
public void draw() {
this.draw( 1.0 );
}
}
public class ColorableRectangle extends Rectangle {
public ColorableRectangle( int height, int width, Color c ) {
super( height, width );
defaultColor = c;
}
public void draw( double scale ) {
draw( defaultColor, scale );
}
public void draw( Color c, double scale ) {
super.draw( scale )
// graphics code to fill in the rectangle with the color
// c would go here.
}
private Color defaultColor;
}
Solution:
public class Rectangle {
public Rectangle( int height, int width ) { ... }
public void draw( double scale ) { ... }
overloading
public void draw() {
this.draw( 1.0 );
}
}
this is a polymorphic variable
public class ColorableRectangle extends Rectangle {
public ColorableRectangle( int height, int width, Color c ) {
super( height, width );
defaultColor = c;
overriding
}
public void draw( double scale ) {
draw( defaultColor, scale );
}
public void draw( Color c, double scale ) {
super.draw( scale )
// graphics code to fill in the rectangle with the color
// c would go here.
}
private Color defaultColor;
}
Access Control
Java provides three visibility modifiers, so that you can
control which classes can access which attributes of a class:
public: the attribute is accessible from any code
private: the member is only accessible to code that is part of the
class which the attribute is part of.
protected: equivalent to private, except that subclasses of the class
which defines the attribute are also allowed access.
There is another type of access, refered to as package or
friendly access. We will talk about this more in the next
Java section of the course.
Example
public class VisibilityExample {
public int
x = 1;
private String
s = “secret”;
protected boolean b = true;
public boolean getB() {
return b;
}
public boolean setB( boolean b ) {
this.b = b;
}
protected String spillTheBeans() {
return s;
}
protected String changeTheSecret( String newSecret ) {
s = newSecret
}
}
public class ExampleSubClass extends VisibilityExample {
public void DoSomething() {
// String theSecret = s;
// can't do this!
String theSecret = spillTheBeans(); // but can do this.
changeTheSecret( “42” );
// and can even do this!
b = false;
x = x + 1;
}
}
Example cont'd
public class VisibilityExample {
public int
x = 1;
private String
s = “secret”;
protected boolean b = true;
public boolean getB() {
return b;
}
public boolean setB( boolean b ) {
this.b = b;
}
protected String spillTheBeans() {
return s;
}
protected String changeTheSecret( String newSecret ) {
s = newSecret
}
}
public class MoreExample {
public static void main( String args[] ) {
VisibilityExample v = new VisibilityExample();
// String theSecret = v.s;
// can't do this!
// String theSecret = v.spillTheBeans();
// can't do this either!
// System.out.println( “v.b = “ + v.b );
// can't even do this
System.out.println( “v.b = “ + v.getB() ); // but we CAN do this
v.setB( false );
v.x = v.x + 1;
}
}
Appropriate uses of visibility modifiers within a
class
public
constants for client programmers,
e.g. public static final int MAX_USERS = 1000;
helper methods
any state data that is not purposefully being exposed.
protected
interface methods
private
defensive programming: strive to idiot proof your classes –
make it so that it is not possible to use them incorrectly
methods for maintaining object state
internal data structures, only if they are being purposefully exposed.
In practical terms, this means the authors of subclasses will need
access to the source and/or meticulous documentation.
Single vs. Multiple inheritance
single inheritance results in an inheritance tree, because
each class has at most one parent
multiple inheritance allows multiple parents, resulting in an
inheritance graph
Shape is a “mixin class”,
designed to augment
another (okay)
The widgets form a
diamond inheritance
structure (BAD)
Java does not allow a class to extend more than one parent
class because:
Multiple inheritance in Java
diamond inheritance (e.g. SuperBrokenWidget) creates ambiguity
which is hard to resolve
inheritance graphs are much more difficult to understand and design
than inheritance trees
Java does allow a class to implement more than one
interface
as we will see later, there are techniques that allow us to
achieve the code reuse benefits of multiple inheritance with
less of the complexity in Java
class inheritance
an object's class defines how it is implemented
define an object's implementation in terms of another object by
reusing, replacing and refining parent methods
is a mechanism for code reuse
interface inheritance (subtyping)
Interface vs Implementation Inheritance
an object's type(s) define only it's interface
represents where one object can be used in place of another
principle of substitutability
in practice, we do both when extending a Java class, though
only the later when implementing an interface
Don't overuse inheritance!
defines static, compile time relationships
can lead to situations where objects need to “morph”
classes (which is not possible in strongly typed languages
like Java)
deep, complex hierarchies of objects create more
complexity than they eliminate
composition does not support polymorphism
composition is marginally slower
inheritance usually results in less new code to write and
debug.
composition does not result in dependencies between the
classes involved
composition is simpler for client programmers to understand:
Composition vs. Inheritance
large inheritance hierarchies are difficult to understand
inherited attributes of an object are implicit, with composition all
attributes are explicit. This can lead to the “yo-yo” problem, where
the client programmer has to flip back and forth between
documentation for several classes
inheritance cannot hide inherited attributes from client
programmers
empircally, composition usually results in better reuse
The result...
In practice, we use both inheritance and composition, often
together, to achieve good quality object oriented programs.
Window Widgets Example
Suppose we were building a general purpose, portable
windowing library.
Using just inheritance, we would arrive at a structure like
this:
What's wrong?
client code has to choose the
window type (we could fix this
with a factory method though)
adding a new platform (e.g.
Win32Window), means
subclassing every kind of
window (Window,
IconWindow, DialogWindow,
ModalWindow...)
no good solution to the latter
problem using inheritance
Widgets Example Cont'd
the solution is to separate the windowing implementations
from the windowing functionality
Window, and it's subclasses, define different kinds of
windows in terms of an abstract window implemenation,
WindowImp
Different platforms provide different WindowImp subclasses
by using composition and inheritance, we have a much
more flexible and clear system
Delegation
can be used to replace inheritance with composition
to be entirely equivalent to inheritance, the delegate must
receive a reference to the caller
combined with interface inheritance, delegation gives us the
benefits of multiple inheritance (e.g. use of mixin classes),
but with less complexity in the derived class
Next lecture: abstract classes, and then on to
exceptions and exception handling...