| CSCI A201/A597 Lecture Notes Twenty-Nine
Second Summer 2000
|
Extending Classes II: Overriding & Shadowing.
| A class gives us a description, a list of features that an object of that class should have.
| By features we mean what kind of data members and method members it should have. |
|
By extending a class we extend our original description, and specify additional features.
|
An object of the extended class has all the features
listed in the original class and the class that extends it.
|
|
An object of the original class has the features listed in the original class.
|
That's what we started from, anyway.
|
This is the reason for which we say that if class B extends
class A every object of type B is also an object
of type A.
|
Just put on a pair of blinkers.
|
But an object of type A is not of type
B (objects of type B have additional features,
listed in the description of class B).
|
Objects of type B are more specialized.
|
|
Another way to remember this is in this way:
|
... if Goalkeeper extends Player we can
use an object of type Goalkeeper everywhere we need
to use a Player
|
... any Player
|
... but not the other way around.
|
|
This is one kind of polymorphism.
|
In our discussion in class we have distinguished between:
- object references
- actual objects
|
| Both have a type. |
Objects are anonymous, and we refer to them by names
(or object references). |
|
Extending classes is a very simple concept.
|
Creating composite objects from composite descriptions is as easy as
putting the two descriptions together,
|
|
... unless we use the same names for different features
|
(class members of the same kind: data, or methods)
|
|
in the two descriptions.
|
- when the name of two methods collide overriding happens.
- when variables are involved it's called shadowing.
|
|
A few examples will help clarify these concepts.
|
|
class Player {
void fun() {
System.out.println("Having fun as a Player.");
}
}
class Goalkeeper extends Player {
void fun() {
System.out.println("Having fun as a Goalkeeper.");
}
}
public class One {
public static void main(String[] args) {
System.out.println("Welcome to Program One.");
Player meola = new Goalkeeper();
meola.fun();
Goalkeeper higuita = new Goalkeeper();
higuita.fun();
((Player)higuita).fun();
}
}
|
Here's what you get when you run the program:
|
You see then,
|
frilled.cs.indiana.edu%javac One.java
frilled.cs.indiana.edu%java One
Welcome to Program One.
Having fun as a Goalkeeper.
Having fun as a Goalkeeper.
Having fun as a Goalkeeper.
frilled.cs.indiana.edu%
|
... that it's the type of the object
(and not that of its reference) that really matters.
|
If either A or B do not define
fun() then there's no overriding involved,
and no dynamic method lookup is involved.
|
|
But it is instructive to cover the three alternatives to the situation described above.
|
In total: four cases.
|
Case 2. Player does not define fun().
|
Only objects of type Goalkeeper referenced through object references of type Goalkeeper will be able
to invoke fun().
|
Casting an object of type Goalkeeper to type Player and asking for fun()
|
... will get you into trouble early (as early as compile time).
|
Case 3. Goalkeeper does not define fun().
|
Objects of type Goalkeeper inherit fun() from being Players (but
they don't have own fun).
|
Case 4: Neither Player nor Goalkeeper define fun().
|
In that case there's really nothing to talk about.
|
|
To summarize, looking up a method for invocation involves taking into account the
class of the object and the class of its reference (casting included here).
|
The object reference's class determine the method unless the object's class also
defines a method with the same name, in which case that's the one that will be invoked.
|
class Player {
void fun() {
System.out.println("Having fun as a Player.");
}
}
class Defender extends Player {
}
class Fullback extends Defender {
void fun() {
System.out.println("Having fun as a Fullback.");
}
}
public class Two {
public static void main(String[] args) {
System.out.println("Welcome to Program Two");
Defender dooley = new Defender();
dooley.fun(); // fun inherited (Player)
Fullback baresi = new Fullback();
baresi.fun(); // Fullback has its own fun
Defender tresor = new Fullback();
tresor.fun(); // type Fullback overrides the fun that
// Defender inherited from Player
Player rijkaard = new Fullback();
rijkaard.fun(); // the fun defined in Fullback is used
// over the one defined in Player, since
// the type of the object rijkaard points
// to (that class) has own fun
}
}
frilled.cs.indiana.edu%java Two
Welcome to Program Two
Having fun as a Player.
Having fun as a Fullback.
Having fun as a Fullback.
Having fun as a Fullback.
frilled.cs.indiana.edu%
Once you override a function the only way you can get to it is through a super reference.
|
You can't get to it from outside the class (through casting).
|
|
Overriding is a stronger notion than shadowing.
|
If you shadow a variable you can still get to it from outside by casting.
|
|
But we don't want to talk about shadowing here.
|
We never used it this semester.
|
|
We have used overriding.
|
We redefined paint.
|
Let's simulate how paint works.
|
Indeed, let's analyze that.
|
|
Before we do that, let's make two final points.
|
One refers to super.
|
super is a kind of casting.
|
It allows us to put one pair of blinkers right away.
|
this is a reference to the current object.
|
... of the same type as the object's class.
|
|
And now the program.
|
Can you annotate it and explain what happens?
|
class Frame {
protected String myGC = "The Graphics Context from class Frame";
protected int width, height;
protected boolean visible;
protected void resize(int w, int h) {
setSize(w, h);
refresh();
}
protected void refresh() {
paint(myGC);
}
protected void setVisible(boolean tF) {
visible = tF;
paint(myGC);
}
protected void setSize(int w, int h) {
width = w;
height = h;
}
public void paint(String gc) {
System.out.println("Frame: I use\n " + gc + " \nto draw my images.");
}
}
public class Window extends Frame {
public void paint(String gc) {
System.out.println("Window: I use\n " + gc + " \nto draw my images.");
}
public static void main(String[] args) {
Frame f = new Window(); // you have
f.setSize(100, 200); // seen this
f.setVisible(true); // many times...
user(f); // you never ever see this
// but you know it happens
}
private static void user(Frame f) {
f.resize(200, 400);
// minimal interaction by the user simulated here
}
}
- no packages are used
- we are simulating a
Frame
- we simulate the
user too
|
If you understand how the code above works then
|
|
|
|
- you understand overriding of methods
- you understand why we need to override
paint when doing graphics
- you understand how
paint gives you access to a graphics context
|
Also note: protected is like private,
|
but allowing inheritance of the variable or method.
|
Last updated: August 8, 2000 by Adrian German for A201