ITEC120-ibarland (incl. office hrs)—info—lectures—labs—hws—java.lang docs—java.util docs
Inheritance
- class B extends A;
we say that B is the “subclass”,
and
A is the “superclass”.
Also: Every B “is-a” A.
Note the arrow in BlueJ — different from the “has-a”
(whose arrowhead isn't closed).
(click for .jar)
-
Call polymorphically.
-
If a superclass has been declared as an abstract class,
it means you can't have objects which are the supertype without
actually being some specific subtype.
That is: you can't call their constructor directly.
-
constructor
-
You can have actual code and fields
in a superclass (unlike an interface).
-
We've actually already seen inheritance:
all classes implicitly extend Object.
-
We can add behavior specific to new classes;
for instance, perhaps Dogs can wagTail
(which Cats are simply not able to do).
(click for .jar)
-
Overriding.
(click for .jar)
-
Note that we've seen overriding already:
we've overriden toString.
Similarly, equals just uses == by default,
but some classes (like String)
have overridden equals to do something more specific.
Don't confuse “overriding” with the
much shallower concept, “overloading”.
-
(Advanced): What if you are overriding a method (say,
SmallDog's speak),
and in the process of doing a SmallDog's
speak you want to do a regular old speak?
If you just call speak, that's the same as this.speak,
which is the very method you're in the middle of writing;
you don't want to call yourself!
Solution: use super.speak()
(as opposed to this.speak()).
In Java, every non-static method has two variables which are automatically
provided for you: this (a reference to yourself)
and super (a reference to yourself, but in the guise
of your superclass).
Note that these two words are also abused a bit: they are also
the keywords used to call a constructor from within a constructor.
-
Caution:
In Java,
You can't override fields.
If you include a field name down in class Cat,
then polymorphic code (which is using a variable of
type Animal will use the field from inside Animal,
not the one from inside Cat!
(But code which knows its variable is declared type is
Cat will use the field inside
the subclass.)
Upshot: Don't override fields; it causes hard-to-find bugs
and most Java compilers don't give you any warnings.
Interface vs. abstract class
- An interface contains only method signatures
(and static final constants).
- An (abstract) class can contain fields and actual code,
which gets shared.
- A class can only extend one superclass.
- A class can implement many superclass.
The first two are an advantage to abstract classes,
but the second two are an advantage to interfaces.
We'll come back to this topic below, trying to get the best
of both worlds.
The Delegate Pattern
How to fake multiple inheritance, if Java doesn't allow it?
That is, suppose we have Guard Dogs,
which are like Dogs but they have some
additional guard behaviours:
String threatenSound(); // How does this guard threaten?
boolean deters(Animal anAnimal); // Does this guard succesfully deter anAnimal?
|
In fact, we quickly realize that being a guard is not specific to
being a Dog; we could certainly have
guard-cats and guard-pythons.
In fact, some guards aren't even animals -- an alarm system
can make a threatening sound and can deter some (human) animals.
So we should have Guard be its own class.
(There is code for the Guard methods.
But now we have the problem, that for a class GuardDog we
want to Extend two different classes.
-
One solution would be to choose one of the two classes to extend, and
the other to implement:
(click for .jar)
This still results in a lot of repeated code:
for whichever class we chose to make a mere interface (here, IGuard),
we'll have to any common code that in each other IGuard.
So GuardCat and GuardPython are annoying to write.
-
A better solution to faking multiple inheritance:
The Delegate Pattern.
We'll make class GuardDog2 extends Dog implements IGuard.
But that just brings us back to having repeated code in the interface.
Well, we'll actually keep around class Guard implements IGuard
as well (!).
(click for .jar)
Then, say that every GuardDog2 has a little Guard
inside themselves.
(Note the two types of arrowheads —
most are “is-a”,
but GuardDog2 has-a Guard.
Whenever we walk up to a GuardDog2 and ask it to do
IGuard behavior (say, make a threatenSound()),
that dog will look inside itself, find its inner Guard,
and delegate the task: it will just call threatenSound()
on its inner Guard object; whatever response the
GuardDog2 gets back it will simply relay to whoever called its
method in the first place.
This is better, but not a perfect solution.
For instance:
And, if we want to make a SmallGuardDog,
what class should it extend — GuardDog,
or SmallDog?
Either answer is unsatisfactory.
Delegate pattern can still help, though it's starting to become cumbersome:
for every class we can have an interface,
and then have SmallGuardDog extend SmallDog implement IGuardDog2,
and it will have a GuardDog2 delegate instance inside of it.
This avoids repeated code (at the cost of our mental overhead).
ITEC120-ibarland (incl. office hrs)—info—lectures—labs—hws—java.lang docs—java.util docs