home—info—exams—lectures—labs—hws
syntax—Laws—lies—java.lang docs—java.util docs
Interfaces
Review inheritance
-
Inheritance:
If class B extends A, we say “B is-a A”.
Anything a A can do, a B can do too (and possibly more).
For example, because Animals can speak,
then automatically Dogs can speak.
Note that all fields and methods which are common to all subclasses
are actually put inside of the superclass
only once.
That is, the code for speak and the field sound
aren't repeated in each of Cat, Python, Dog,
Narwhale, etc..
Mathematically: the set of Bs is a subset of the set of As.
-
polymorphism:
If a method takes a parameter of type A,
you can actually pass it a B if you want.
For example,
static String threaten( Animal attacker, Animal defender ) {
return attacker.speak() + defender.speak() + attacker.speak();
}
|
You can pass in a Dogs and a Python, no problem,
since they each know how to speak.
You can even pass in subclasses of Animals which we'll invent tomorrow.
As long as it extends Animal, it's guaranteed to speak.
(Note that the method can't say attacker.wagTail(), because
that is a behavior which some Animals have, but not all.)
It's a straightforward concept, but it gets the fancy name “polymorphism”
because it's the first time that we have an object which is constructed as
one type (say, Python), yet declared as another (Animal).
We sometimes speak of the object's “underlying type” and its
“declared type”.
-
Overriding
If we declare a method in a subclass which had already been declared in a superclass,
we can refine its behavior.
This interacts with polymorphism.
This explains how toString works, and why code written at Sun Microsystem labs
can call our toString inside (say) Dog.
We saw this when we printed a LinkedList<Dog>.
(Don't confuse “override” — which is way cool —
with “overload”, which is convenient but not deep.)
(See also:
interfaces
for gRUe's IO)
Interface vs. abstract class
-
An interface is like a class except that it contains no code --
it only has method signatures
(and static final constants).
-
Like an abstract class, interfaces are useful only for extending;
you can't make an instance of the interface itself.
- By comparison, 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.
An example:
the Comparable
interface.
Similarly,
java.util.List.
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).
home—info—exams—lectures—labs—hws
syntax—Laws—lies—java.lang docs—java.util docs