A Drawing that holds a list of polygons and draws them all, an animation that makes them blink, and then the problem: to draw anything other than a polygon the code must be generalised, but DrawablePolygon already extends Polygon. Interfaces are Java's answer, and polymorphism is the name for what has been happening all along. Follows Think Java 2e, Chapter 17 (Advanced Topics), Sections 17.5-17.7, pp. 283-290, cross-referenced against The Java Tutorials — Defining an Interface.
Subject: Java · 65 slides · code lesson
Open the interactive version of this deck
Title
Think Java 2e · Chapter 17 · Advanced Topics
Sections 17.5-17.7 · pp. 283-290
Objectives
This lesson follows Think Java 2e, Chapter 17 (Advanced Topics), Sections 17.5-17.7, pp. 283-290. Everything on these slides can be checked against those pages.
1. Build a canvas that holds a list of drawable objects and paints them all.
2. Define polymorphism and identify a polymorphic method and a polymorphic data structure.
3. Add per-object animation with a step method overridden in subclasses.
4. Explain what super.draw(g) does when the immediate superclass has no draw method.
5. Distinguish an interface from an abstract class.
6. Write a class that extends one class and implements an interface.
Warm-up
Three facts, all about to become the chapter's argument.
Discussion prompt
From Lesson 14a: how many superclasses may a Java class extend, and why can a Hand be passed where a CardCollection is expected? And from Lesson 16: what is the difference between an abstract class and a concrete one?
Hint: Exactly one. And an abstract class cannot be instantiated.
Answer:
Classes may extend only one superclass. A Hand can be passed where a CardCollection is expected because a Hand is one. And an abstract class cannot be instantiated and may declare methods without bodies, while a concrete class implements all of its methods.
The first fact is the wall this lesson runs into. DrawablePolygon already extends Polygon, and classes can extend only one superclass — which is precisely the problem interfaces solve.
Concept
A Drawing holds objects and draws them. The whole lesson is about widening what objects is allowed to mean.
Figure (svg): Three panels showing the Drawing list widening from polygons to any drawable actor
Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 17 (Advanced Topics), Sections 17.5-17.7, pp. 283-290 — Section 17.5 opens on printed page 283.
Section
Section 17.5
Concept
Now that we have DrawablePolygon and RegularPolygon, let's take them for a test drive. We'll need a Canvas for drawing them, so we define a new class, Drawing, that extends Canvas.
public class Drawing extends Canvas {
private ArrayList<DrawablePolygon> list;
public Drawing(int width, int height) {
setSize(width, height);
setBackground(Color.WHITE);
list = new ArrayList<DrawablePolygon>();
}
public void add(DrawablePolygon dp) {
list.add(dp);
}
public void paint(Graphics g) {
for (DrawablePolygon dp : list) {
dp.draw(g);
}
}
}| member | does |
|---|---|
| extends Canvas | inherits the drawing machinery |
| the ArrayList | holds the objects to draw |
| the constructor | sets the size and background, creates the empty list |
| add(dp) | appends one polygon |
| paint(g) | overrides Canvas's, and draws every element |
The Drawing class has an ArrayList of DrawablePolygon objects. When we create a Drawing object, the list is initially empty. This is Lesson 15a's GridCanvas with a list instead of a grid — the same specialization of the same library class, for a different shape of data.
Notation
The third paint override in three chapters, and by now the pattern should read at a glance.
Annotate
paint; you never do — Lesson 15a's rule, unchanged.Delegating drawing to the objects is what makes the container reusable. Drawing would work unchanged if polygons were replaced by anything else that could draw itself — which is the observation Section 17.7 acts on.
Worked example
Here is an example that creates three RegularPolygon objects and draws them.
public static void main(String[] args) {
// create some regular polygons
DrawablePolygon p1 = new RegularPolygon(3, 50, Color.GREEN);
DrawablePolygon p2 = new RegularPolygon(6, 50, Color.ORANGE);
DrawablePolygon p3 = new RegularPolygon(360, 50, Color.BLUE);
// move them out of the corner
p1.translate(100, 80);
p2.translate(250, 120);
p3.translate(400, 160);
// create drawing, add polygons
Drawing drawing = new Drawing(500, 250);
drawing.add(p1);
drawing.add(p2);
drawing.add(p3);
}| polygon | sides | colour | moved to |
|---|---|---|---|
| p1 | 3 | GREEN | (100, 80) |
| p2 | 6 | ORANGE | (250, 120) |
| p3 | 360 | BLUE | (400, 160) |
Create the shapes.
Why: The first block of code creates RegularPolygon objects with 3, 6, and 360 sides.
Move them apart.
Why: The second block of code translates the polygons to different locations — they are all built at the origin.
Add them to the Drawing.
Why: The third block of code creates the Drawing and adds the polygons to it.
Show the window.
Why: And the fourth block of code creates a JFrame, adds the Drawing to it, and displays the result.
Verify: Look at the third shape: as you can see, a polygon with 360 sides is a pretty good approximation of a circle.
Why: Most of these pieces should be familiar, but one part of this program might surprise you. Look again at the declared types on the first three lines — the variables are DrawablePolygon and the objects are RegularPolygon.
Prediction
The variable is the superclass; the object is the subclass.
DrawablePolygon p1 = new RegularPolygon(3, 50, Color.GREEN);| RegularPolygon extends | so it is also |
|---|---|
| DrawablePolygon | ? |
Predict first
Does it compile?
Correct: Yes — every RegularPolygon is also a DrawablePolygon
Why: This is polymorphism: a language feature that allows objects to be assigned to variables of related types. The object genuinely has every DrawablePolygon method, so the variable's promise is kept — the same rule that let a Hand be passed where a CardCollection was expected in Lesson 14a.
Concept
When we create the RegularPolygon objects, we assign them to DrawablePolygon variables. It might not be obvious why that's legal.
DrawablePolygon p1 = new RegularPolygon(3, 50, Color.GREEN);| is polymorphic because | |
|---|---|
| Drawing.add | the parameter can be one of many types |
| the ArrayList in Drawing | the elements can be different types |
| the variable p1 | it refers to an object of a subclass |
polymorphism — A language feature that allows objects to be assigned to variables of related types.
RegularPolygon extends DrawablePolygon, so every RegularPolygon object is also a DrawablePolygon. The parameter of Drawing.add has to be a DrawablePolygon, but it can be any type of DrawablePolygon. Polymorphism is a fancy word that means having many forms — and it is Lesson 14a's substitution rule, finally named.
Trap
The declared type limits what you can call.
DrawablePolygon p = new RegularPolygon(6, 50, Color.BLUE);
p.draw(g); // fine - DrawablePolygon has draw
p.getNumSides(); // compile error, even though the object has it| declared type | actual object | |
|---|---|---|
| p | DrawablePolygon | RegularPolygon |
| methods you may call | DrawablePolygon's | — |
| methods the object has | — | RegularPolygon's too |
The object really is a RegularPolygon; the compiler just does not know it. It checks against the declared type, because at compile time that is all it can be sure of.
Declare the type you need to use.
// if you only need the shared behaviour:
DrawablePolygon p = new RegularPolygon(6, 50, Color.BLUE);
// if you need RegularPolygon's own methods:
RegularPolygon rp = new RegularPolygon(6, 50, Color.BLUE);| declare as | when |
|---|---|
| the superclass | you only need shared behaviour — the usual case |
| the subclass | you need something only it provides |
| the loosest type that works | the general rule |
The book declares them as DrawablePolygon deliberately, because all it does is translate and add them — both DrawablePolygon operations. Declaring the general type documents what you actually depend on, which is Lesson 14a's ask for the least you need.
Definition probe
A method or a structure can be.
Sort into buckets
Sort each item.
Fill the middle
Each object draws itself.
Fill in the blanks
public void paint(Graphics g) list}) draw}(g);
}
}
Why: Drawing overrides paint and delegates the actual drawing to each object, passing the Graphics along. The enhanced for loop is right because no index is needed — and the delegation is what will let the list hold something other than polygons in Section 17.7.
Socratic
The object really does have the method.
Discussion prompt
p.getNumSides() fails even though p refers to a RegularPolygon. Why does Java check the declared type rather than the actual object?
Hint: What can the compiler know for certain?
Answer:
Because at compile time the actual object is not knowable in general. p could be assigned a plain DrawablePolygon on the next line, or come from a method that returns one.
The declared type is a promise about every object the variable could ever hold, and the compiler checks against that promise.
Which is what makes polymorphism safe. A method taking a DrawablePolygon can rely on every DrawablePolygon method existing on whatever it receives — that guarantee is exactly what the restriction buys.
Section
Section 17.6
Concept
At this point, we have a simple program that draws polygons; we can make it more fun by adding animation. Chapter 15 introduced the idea of simulating time steps.
while (true) {
drawing.step();
try {
Thread.sleep(1000 / 30);
} catch (InterruptedException e) {
// do nothing
}
}| line | does |
|---|---|
| drawing.step() | update every object, then repaint |
| Thread.sleep(1000 / 30) | about 33 milliseconds |
| the effect | roughly 30 frames per second |
Each time through the loop, we call step to update the Drawing. Then we sleep with a delay calculated to update about 30 times per second. The same loop as Lesson 15b and Lesson 16 — the third appearance of update, repaint, sleep.
Picture it
One call at the top becomes one call per object, and then a repaint.
Figure (svg): A pipeline from the animation loop through Drawing step to each polygon's own step and a repaint
It invokes step on each DrawablePolygon in the list and then repaints (clears and redraws) the canvas. Drawing knows when to update and nothing about what updating means — which is why the same container can animate a blinking polygon and a moving sprite.
Worked example
In order for this code to compile, we need DrawablePolygon to provide a step method. Here's a version that doesn't do anything; we'll override it in subclasses.
// in DrawablePolygon:
public void step() {
// do nothing
}
// in Drawing:
public void step() {
for (DrawablePolygon dp : list) {
dp.step();
}
repaint();
}| class | step does |
|---|---|
| DrawablePolygon | nothing |
| RegularPolygon | nothing — inherited |
| BlinkingPolygon | counts, and toggles visibility |
The loop calls dp.step().
Why: So the declared type must have a step method, or it will not compile.
DrawablePolygon supplies an empty one.
Why: A polygon that does not animate simply does nothing on a time step.
Subclasses override it.
Why: Which is what makes one shape blink while another sits still.
Compare with Lesson 16's abstract.
Why: There, an empty method was the wrong choice; here, doing nothing is a legitimate behaviour.
Verify: Ask why update in Automaton was abstract and step here is not.
Why: Because a static polygon genuinely has nothing to do, so the empty body is correct rather than a placeholder — whereas an Automaton with no update would be a broken simulation. **Abstract is for methods with no sensible default; an empty body is for methods whose default is nothing.**
Prediction
BlinkingPolygon extends RegularPolygon, which has no draw.
public class BlinkingPolygon extends RegularPolygon {
public void draw(Graphics g) {
if (visible) {
super.draw(g);
}
}
}| class | has draw? |
|---|---|
| BlinkingPolygon | yes — this one |
| RegularPolygon | no |
| DrawablePolygon | yes |
Predict first
Whose draw method runs?
Correct: DrawablePolygon's — Java keeps looking up the chain
Why: But the parent class is RegularPolygon, which does not provide a draw method. In this case, super invokes draw from the DrawablePolygon class. super means start looking in the superclass, not the immediate superclass must have it — the search continues upward until a definition is found.
Concept
Now let's design a new type of polygon that blinks. Two new attributes and two overridden methods.
public class BlinkingPolygon extends RegularPolygon {
protected boolean visible;
protected int count;
public BlinkingPolygon(int nsides, int radius, Color c) {
super(nsides, radius, c);
visible = true;
count = 0;
}
public void draw(Graphics g) {
if (visible) {
super.draw(g);
}
}
public void step() {
count++;
if (count == 10) {
visible = !visible;
count = 0;
}
}
}| step number | count | visible |
|---|---|---|
| 1 to 9 | 1 to 9 | true |
| 10 | reset to 0 | false — toggled |
| 20 | reset to 0 | true — toggled back |
The step method increments count. Every 10 time steps, it toggles visible and resets count to 0. At 30 frames per second that is a blink about three times a second — and visible = !visible is Lesson 5b's logical negation doing the toggling.
Trap
An override that replaces instead of extending.
public void draw(Graphics g) {
if (visible) {
// now what? re-implement fillPolygon here?
}
}| approach | result |
|---|---|
| nothing in the if | the polygon is never drawn |
| copy DrawablePolygon's two lines | duplicated code |
| call super.draw(g) | correct |
Overriding replaces the inherited method entirely. If the subclass wants to add a condition around the original behaviour, it has to call the original explicitly.
Guard the condition, then delegate upward.
public void draw(Graphics g) {
if (visible) {
super.draw(g);
}
}| visible | what happens |
|---|---|
| true | super.draw(g) runs — the polygon appears |
| false | nothing is drawn — it vanishes |
The draw method draws the polygon only if it is visible. It uses super to call draw in the parent class. Adding a condition around inherited behaviour is one of the most common reasons to override — and super.method() is how you keep the behaviour you are wrapping.
Prediction
30 frames per second, toggling every 10 steps.
public void step() {
count++;
if (count == 10) {
visible = !visible;
count = 0;
}
}| value | |
|---|---|
| steps per second | 30 |
| steps per toggle | 10 |
Predict first
How many times per second does visibility change?
Correct: 3
Why: Thirty steps a second divided by ten steps per toggle gives three toggles a second — so the polygon appears and disappears about one and a half times a second. Note the frame rate lives in the animation loop and the blink interval lives in the object, so the two can be tuned independently.
Fill the middle
Count up, then flip.
Fill in the blanks
public void step() ++};
if (count == 10) !}visible;
count = 0;
}
}
Why: count++ is the increment from Lesson 6a; !visible is logical negation, which turns true into false and back. Resetting count to 0 is what makes the interval repeat rather than firing once at step ten and never again.
Explain it to yourself
Lesson 16 made a similar method abstract.
Discussion prompt
Automaton declared update abstract; DrawablePolygon gives step an empty body. Why the different choice?
Hint: Is doing nothing a legitimate behaviour in each case?
Answer:
A static polygon genuinely has nothing to do on a time step, so an empty body is the correct behaviour rather than a missing one.
An Automaton with no update would be broken — a simulation that never simulates. There is no sensible default, so the compiler should demand one.
**The test: is do nothing a correct answer for some subclass?** If yes, an empty body is right; if no, make it abstract so a forgotten override fails to compile.
Section
Section 17.7
Concept
You might be getting tired of polygons at this point. Can't we draw anything else? Of course we can, but Drawing is currently based on DrawablePolygon. To draw other types of objects, we have to generalize the code.
// The Drawing class does essentially three things:
// 1. it maintains a LIST of objects
// 2. it invokes the DRAW method on each object
// 3. it invokes the STEP method on each object| what Drawing needs | what it does not need |
|---|---|
| draw(Graphics) | npoints, xpoints, ypoints |
| step() | translate, contains, intersects |
| nothing else | colour, sides, radius |
Two methods. Everything else about DrawablePolygon is irrelevant to Drawing — which means requiring the elements to be DrawablePolygons is asking for far more than is needed.
Notation
So here's one way we could make the code more general — and the book proposes it before rejecting it.
Annotate
DrawablePolygon extends Polygon already, and there is no second extends.Java provides another mechanism for inheritance that solves these problems. The constraint is real and the workaround is a language feature, which is a good sign the constraint was deliberate.
Worked example
We can define Actor as an interface instead of a class.
public interface Actor {
void draw(Graphics g);
void step();
}| class | interface | |
|---|---|---|
| keyword | class | interface |
| method bodies | required | not allowed |
| how many can you inherit? | one | as many as you like |
| keyword to inherit | extends | implements |
An interface contains methods.
Why: Like a class definition, an interface definition contains methods.
But only their declarations.
Why: But it contains only the declarations of the methods, not their implementations.
Like an abstract class — almost.
Why: Like an abstract class, an interface specifies methods that must be provided by subclasses. The difference is that an abstract class can implement some methods; an interface cannot.
No public needed.
Why: All interface methods are public by default, since they are intended to be used by other classes.
Verify: Notice void draw(Graphics g); has no public and no body — just a signature and a semicolon.
Why: The semicolon is doing the same job as in an abstract method declaration. An interface is, in effect, a class where every method is abstract and there are no attributes — which is why Java can relax the one-superclass rule for it.
Prediction
DrawablePolygon would need to extend it.
public class DrawablePolygon extends Polygon { ... }
// and now it should also extend Actor?| rule | consequence |
|---|---|
| one superclass per class | ? |
Predict first
What is the obstacle?
Correct: DrawablePolygon already extends Polygon, and classes can extend only one superclass
Why: There's just one problem: DrawablePolygon already extends Polygon, and classes can extend only one superclass. The book also notes the Actor class seems pointless, since the methods it defines don't do anything — but that is a secondary complaint; the single-inheritance rule is what makes it impossible.
Concept
They overlap enough to be confused and differ in exactly the ways that matter here.
| abstract class | interface | |
|---|---|---|
| can declare methods with no body | yes | yes — only that |
| can implement some methods | yes | no |
| can have attributes | yes | no |
| how many can a class inherit | one | many |
| keyword | extends | implements |
| example | Automaton | Actor |
Automaton had to be an abstract class because it carried run and mainloop — real code the subclasses inherited. Actor can be an interface because it carries nothing but two signatures. The choice follows from whether there is shared implementation to give away.
Trap
Java has no multiple inheritance of classes.
public class DrawablePolygon extends Polygon, Actor {
// compile error
}| attempt | legal? |
|---|---|
| extends one class | yes |
| extends two classes | no |
| extends one class, implements one interface | yes |
| extends one class, implements several interfaces | yes |
Classes may extend only one superclass — Lesson 14a's rule, and the reason the whole Actor plan needed a different mechanism.
extends once; implements as often as you like.
public class DrawablePolygon extends Polygon implements Actor {
// rest of the class omitted
}| clause | gives |
|---|---|
| extends Polygon | the inherited attributes and methods |
| implements Actor | the obligation to provide draw and step |
| both together | DrawablePolygon is a Polygon and an Actor |
To inherit from an interface, you use the keyword implements instead of extends. So it inherits methods from Polygon, and it is required to provide the methods in Actor; namely draw and step — which it already had, so this change costs one line.
Definition probe
The deciding question is whether there is shared code.
Sort into buckets
Sort each requirement.
Fill the middle
Two methods, no bodies.
Fill in the blanks
public interface Actor implements
public class DrawablePolygon extends Polygon ___ Actor ___
Why: An interface is declared with interface rather than class and contains only signatures — no bodies and no public, since interface methods are public by default. A class uses implements for an interface and extends for a superclass, and it can do both at once.
Counterexample
It removes the one-superclass limit.
Discussion prompt
If interfaces can be implemented freely, why does Java still have abstract classes — and why was Automaton one?
Hint: What did Automaton give its subclasses?
Answer:
Because an interface cannot implement anything. Automaton gave Conway and Langton a working run and mainloop — dozens of lines of shared code that an interface could not carry.
It also held a protected attribute, grid. An interface has no attributes at all.
The two solve different problems. An abstract class shares implementation with a family of related classes; an interface states a capability that unrelated classes can each satisfy their own way. Actor is a capability — this thing can be drawn and stepped — which is why it is an interface.
Section
Section 17.7
Concept
In terms of inheritance, DrawablePolygon is both a Polygon and an Actor. So the following assignments are legal.
Polygon p1 = new DrawablePolygon();
Actor a1 = new DrawablePolygon();
Polygon p2 = new RegularPolygon(5, 50, Color.YELLOW);
Actor a2 = new RegularPolygon(5, 50, Color.YELLOW);| object | is a Polygon? | is an Actor? | is a DrawablePolygon? |
|---|---|---|---|
| new DrawablePolygon() | yes | yes | yes |
| new RegularPolygon(...) | yes | yes | yes |
| a plain Polygon | yes | no | no |
And the same is true for subclasses of DrawablePolygon. A RegularPolygon inherits both the extends and the implements — so it is an Actor without saying so, which is what lets Drawing hold one.
Picture it
One chain of classes, and an interface cutting across it.
Figure (svg): A UML diagram showing DrawablePolygon extending Polygon and implementing the Actor interface
Classes may extend only one superclass, but they may implement as many interfaces as needed. Java library classes often implement multiple interfaces. That is why the class hierarchy is a tree while the type graph is not.
Worked example
Generalising the container is now a search and replace.
// before
private ArrayList<DrawablePolygon> list;
public void add(DrawablePolygon dp) { list.add(dp); }
// after
private ArrayList<Actor> list;
public void add(Actor a) { list.add(a); }| what Drawing can now hold | requirement |
|---|---|
| DrawablePolygon | implements Actor |
| RegularPolygon | inherits the implements |
| BlinkingPolygon | inherits it too |
| Sprite — Lesson 17c | implements Actor itself |
| anything at all | with a draw and a step |
Replace the element type.
Why: In the Drawing class, replace DrawablePolygon with Actor.
The loops do not change.
Why: dp.draw(g) and dp.step() are exactly the two methods Actor declares.
Nothing else needs editing.
Why: Drawing never used any other DrawablePolygon method — which is why this works.
The list is now polymorphic across unrelated classes.
Why: Interfaces are another example of polymorphism.
Verify: Check that Drawing never calls translate, addPoint or anything else polygon-specific.
Why: That check is what made the generalization possible. Had Drawing used one polygon method, Actor would have needed to declare it — and a sprite would have had to fake it. Depending only on what you use is what leaves the door open.
Prediction
Only DrawablePolygon says implements.
public class DrawablePolygon extends Polygon implements Actor { }
public class RegularPolygon extends DrawablePolygon { }
Actor a = new RegularPolygon(5, 50, Color.YELLOW);| class | declares implements Actor? |
|---|---|
| DrawablePolygon | yes |
| RegularPolygon | no |
Predict first
Does the assignment compile?
Correct: Yes — RegularPolygon inherits the interface along with everything else
Why: And the same is true for subclasses of DrawablePolygon; these assignments are legal too. A subclass inherits its superclass's interfaces as well as its methods, so every RegularPolygon is an Actor without saying so — which is what lets Drawing hold one.
Concept
a1 and a2 are the same type of variable, but they refer to objects with different types. And similarly with p1 and p2.
Actor a1 = new DrawablePolygon();
Actor a2 = new RegularPolygon(5, 50, Color.YELLOW);
// and in Lesson 17c:
Actor a3 = new Sprite("face-smile.png", 25, 150);| inheritance polymorphism | interface polymorphism | |
|---|---|---|
| related how | a shared superclass | a shared capability |
| the classes | must be in one hierarchy | can be unrelated |
| Sprite and RegularPolygon | share only Object | both are Actors |
Sprite and RegularPolygon have nothing in common — one wraps an image file, the other computes vertices with trigonometry. Yet both can sit in the same list, because an interface describes what a thing can do rather than what it is descended from.
Trap
A concrete class must supply every declared method.
public class Sprite implements Actor {
public void draw(Graphics g) { ... }
// no step method
}
// error: Sprite is not abstract and does not override
// abstract method step() in Actor| Actor declares | Sprite provides |
|---|---|
| draw(Graphics) | yes |
| step() | no — compile error |
Like an abstract class, an interface specifies methods that must be provided by subclasses. The compiler enforces it exactly as it enforced Lesson 16's abstract method — same message, same reason.
Provide them all, even the ones that do nothing.
public class Sprite implements Actor {
public void draw(Graphics g) { ... }
public void step() { ... }
}
// and where a method genuinely has nothing to do:
public void keyTyped(KeyEvent e) {
// do nothing
}| situation | what to write |
|---|---|
| the method matters | the real implementation |
| it does not apply | an empty body with a comment |
| you cannot implement it yet | declare the class abstract |
The middle row is common and Lesson 17c meets it head on: keyTyped is required by KeyListener and unwanted by Sprite, so it gets an empty body. An interface is all-or-nothing — which is the price of the guarantee it gives callers.
Definition probe
The rules differ for the two mechanisms.
Sort into buckets
Sort each statement.
Matching
Four declarations from this lesson.
Match the pairs
Why: The last two are the pair worth keeping straight: an interface can only declare, while an abstract class can declare and implement — which is why Automaton could carry run and mainloop while Actor carries nothing.
Real world
Interfaces can be inherited freely; classes cannot.
Discussion prompt
If a class could extend two superclasses, what problem would arise — and why does the same problem not arise for interfaces?
Hint: What if both parents have a method with the same name?
Answer:
Ambiguity. If both superclasses define draw, which one does the subclass inherit? And if both have a field called count, are there two, or one?
Interfaces have no bodies and no fields, so there is nothing to conflict. Two interfaces both declaring draw is fine — the class supplies one implementation that satisfies both.
Java traded a feature for a simpler rule, and then gave back most of the usefulness through interfaces. That is why implements has no limit while extends has one — the restriction exists exactly where the ambiguity would.
Section
Sections 17.5-17.7 together
Concept
Drawing began holding polygons and ends holding anything that can draw and step. Nothing inside it changed except one type name.
// before: a container of a particular class
private ArrayList<DrawablePolygon> list;
// after: a container of a capability
private ArrayList<Actor> list;| before | after | |
|---|---|---|
| what can go in | polygons | anything with draw and step |
| what Drawing knows | the same two methods | the same two methods |
| lines changed | — | two |
| what made it possible | — | Drawing used only those two methods |
The generalization was cheap because the coupling was already loose. Drawing never called a polygon method — which was not an accident but the consequence of letting each object draw itself.
Notation
Chapters 14, 16 and 17 each added a mechanism, and they are not interchangeable.
Annotate
Choose by what you have to give away. Shared code means a class; a bare capability means an interface; both means an abstract class.
Worked example
public class DrawablePolygon extends Polygon implements Actor says three things at once.
public class DrawablePolygon extends Polygon implements Actor {
// rest of the class omitted
}| clause | gives | demands |
|---|---|---|
| extends Polygon | npoints, addPoint, translate, contains | nothing |
| implements Actor | nothing | draw and step |
| the class body | color, draw, step | — |
extends comes first, and there is one.
Why: Java's syntax requires that order.
implements may list several.
Why: Separated by commas — Lesson 17c's Sprite implements two.
The class must satisfy the interface.
Why: Here it already did, since draw and step were written in Section 17.2 and 17.6.
And it gains a second type.
Why: DrawablePolygon is both a Polygon and an Actor.
Verify: Count the types a RegularPolygon object belongs to: RegularPolygon, DrawablePolygon, Polygon, Actor, Object.
Why: Five types for one object, and a variable of any of them can hold it. That is what polymorphism means in practice — and the reason a method should declare the loosest of the five that does its job.
Definition probe
Superclass, abstract class, or interface.
Sort into buckets
Sort each situation.
Concept
The interface was introduced to make room for something that is not a polygon at all.
| class | what it is | implements |
|---|---|---|
| DrawablePolygon | a shape with vertices | Actor |
| Sprite | an image read from a file | Actor, KeyListener |
| what they share | nothing | the ability to draw and step |
Now that our Drawing is based on Actor instead of DrawablePolygon, we can draw other types of graphics. Sprite implements two interfaces, which is where the as many as needed rule stops being theoretical.
Trap
Every implementer must provide every method.
public interface Actor {
void draw(Graphics g);
void step();
void translate(int dx, int dy); // polygons have this
Color getColor(); // sprites do not
}| implementer | translate | getColor |
|---|---|---|
| DrawablePolygon | inherited from Polygon | natural |
| Sprite | would have to invent one | meaningless |
| result | — | empty methods everywhere |
A Sprite has no colour, so getColor would return null or grey and mean nothing. An interface that demands more than every implementer can honestly provide forces its implementers to lie.
Declare only what every implementer genuinely does.
public interface Actor {
void draw(Graphics g);
void step();
}| method | does every actor do it? |
|---|---|
| draw | yes — that is what it is for |
| step | yes — even if the step does nothing |
| translate | no |
| getColor | no |
Two methods, because Drawing needs two. An interface is a contract, and a small contract is easier to satisfy honestly — which is the same argument as Lesson 11a's small public surface, applied to a type rather than a class.
Prediction
Count the whole chain, plus the interface.
class BlinkingPolygon extends RegularPolygon
class RegularPolygon extends DrawablePolygon
class DrawablePolygon extends Polygon implements Actor| type | from |
|---|---|
| BlinkingPolygon | itself |
| RegularPolygon | extends |
| Actor | inherited implements |
Predict first
Which variable types can hold a BlinkingPolygon?
Correct: BlinkingPolygon, RegularPolygon, DrawablePolygon, Polygon, Actor and Object
Why: An object belongs to its own class, every class above it, every interface those classes implement, and Object at the top. Six types for one object — and a method should declare whichever of them is the loosest that still lets it do its job.
Fill the middle
Depend on the capability, not the class.
Fill in the blanks
private ArrayList<Actor> list;
public void add(Actor a) ___
Why: Replacing DrawablePolygon with Actor is the entire generalisation, because Drawing only ever called draw and step — the two methods the interface declares. Anything that implements Actor now fits, including classes with no relationship to polygons at all.
Explain it to yourself
Two methods, no more.
Discussion prompt
Actor declares exactly draw and step. What decided that number, and what would go wrong if it were larger?
Hint: What does Drawing actually call?
Answer:
Drawing calls exactly two methods, so those are the two the interface must declare. The size of the contract was determined by the consumer, not by the implementers.
A larger interface would exclude implementers. A Sprite has no colour and no vertices, so any method about those would force it into a meaningless implementation.
Declare what the caller needs, not what the implementers happen to have. A small interface is easy to satisfy honestly, and every method you add narrows the set of classes that can implement it.
Comparison
Fill the blanks.
Comparison matrix
| superclass | abstract class | interface | |
|---|---|---|---|
| can implement methods | yes, all | yes, some | no |
| can have attributes | yes | yes | no |
| can be instantiated | yes | no | no |
| how many per class | one | one | as many as needed |
| keyword to inherit | extends | extends | implements |
The fourth row is the one that decided this chapter. DrawablePolygon had already spent its one extends on Polygon, so only an interface could add the Actor type on top.
Pattern
Depend on a capability, not a class — then anything that has the capability fits.
// 1. name the capability the consumer needs
public interface Actor {
void draw(Graphics g);
void step();
}
// 2. the consumer holds the interface, not a concrete class
public class Drawing extends Canvas {
private ArrayList<Actor> list;
public void add(Actor a) { list.add(a); }
public void paint(Graphics g) {
for (Actor a : list) { a.draw(g); }
}
}
// 3. anything at all can implement it
public class DrawablePolygon extends Polygon implements Actor { }
public class Sprite implements Actor, KeyListener { }| rule | reason |
|---|---|
| declare only what the consumer calls | a small contract is easy to satisfy |
| extends once, implements freely | only interfaces can be combined |
| a subclass inherits its parent's interfaces | RegularPolygon is an Actor for free |
| a concrete class must supply every method | even if the body is empty |
| super.method() searches upward | not just the immediate parent |
Check
Work it out before you click.
class DrawablePolygon { public void draw(Graphics g) { ... } }
class RegularPolygon extends DrawablePolygon { /* no draw */ }
class BlinkingPolygon extends RegularPolygon {
public void draw(Graphics g) {
if (visible) { super.draw(g); }
}
}| class | defines draw? |
|---|---|
| RegularPolygon | no |
| DrawablePolygon | yes |
Check your understanding
Which draw does super.draw(g) invoke?
Answer: A
Why: But the parent class is RegularPolygon, which does not provide a draw method. In this case, super invokes draw from the DrawablePolygon class. super means begin the search in the superclass, and the search continues upward — the same lookup that lets an inherited method be called without qualification.
super. explicitly skips the current class, which is what prevents recursion.Check
Work it out before you click.
public interface Actor {
void draw(Graphics g);
void step();
}| feature | interface |
|---|---|
| method bodies | ? |
| attributes | ? |
Check your understanding
What is the difference from an abstract class?
Answer: A
Why: Like an abstract class, an interface specifies methods that must be provided by subclasses. The difference is that an abstract class can implement some methods; an interface cannot. That is why Automaton had to be an abstract class — it carried run and mainloop — while Actor can be an interface, and why only Actor could be added to a class that already extended Polygon.
abstract allows in a class.Check
Work it out before you click.
DrawablePolygon p = new RegularPolygon(6, 50, Color.BLUE);
drawing.add(p);
// public void add(DrawablePolygon dp)| type | |
|---|---|
| the variable | DrawablePolygon |
| the object | RegularPolygon |
Check your understanding
What makes this legal?
Answer: A
Why: Polymorphism: a language feature that allows objects to be assigned to variables of related types. Nothing is converted — the object genuinely has every DrawablePolygon method, because it inherited them. Drawing.add is a polymorphic method for the same reason: the parameter can be one of many types.
add method, taking the superclass.Real world
Drawing can display objects whose classes did not exist when Drawing was written.
Discussion prompt
Where else does a program have to work with types its author never saw — and how does an interface make that possible?
Hint: Every plugin, every callback, every sort.
Answer:
Anywhere a library calls your code. Arrays.sort compares objects it has never seen by requiring them to implement Comparable — the same idea as Actor, with compareTo instead of draw.
And every plugin system: the host declares an interface, and anyone can write a class that implements it. The host was compiled long before the plugin existed.
Lesson 17c is the next example — KeyListener is an interface the window system defines and your class implements, so that the system can call you when a key is pressed. An interface is how code you have not written yet gets to participate.
Commit first
Commit to an answer and to your confidence.
Predict first
Why did Actor have to be an interface rather than a superclass?
Correct: DrawablePolygon already extends Polygon, and a class may extend only one superclass
Why: There's just one problem: DrawablePolygon already extends Polygon, and classes can extend only one superclass. The book raises a second objection too — the Actor class seems pointless, since the methods it defines don't do anything — but that one is a matter of taste; the single-inheritance rule makes the superclass approach impossible outright. Since classes may implement as many interfaces as needed, extends Polygon implements Actor costs one clause and no restructuring — and Sprite in Lesson 17c goes further, implementing two interfaces at once.
Explain it
Two minutes, out loud.
Discussion prompt
A classmate asks when to use an interface and when to use an abstract class. Give them the rule and one example of each from this course.
Hint: What do you have to give away?
Answer:
Ask what you have to give the subclasses. If there is real shared code, you need a class — Automaton gave Conway and Langton a working run and mainloop, which an interface could not carry.
If there is nothing to give and only methods to require, use an interface — Actor just says a thing can draw and step, and carries no code and no fields.
And there is a hard constraint: a class can extend only one superclass but implement any number of interfaces. So if the class already extends something, the interface is your only option. That constraint decides more designs than the philosophy does.
Exit ticket
One question before you close the deck.
Predict first
What is polymorphism?
Correct: A language feature that allows objects to be assigned to variables of related types
Why: Polymorphism: a fancy word that means having many forms. A RegularPolygon can be held in a DrawablePolygon variable, an Actor variable, or a Polygon variable — one object, several types. That is what makes Drawing.add a polymorphic method (the parameter can be one of many types) and its ArrayList a polymorphic data structure (the elements can be different types). The second option describes overloading, the third an abstract class, and the fourth generalization — three different ideas that are easy to confuse with this one.
Connect it up
One page, from memory.
Draw it
Draw the type graph: Polygon, DrawablePolygon, RegularPolygon, BlinkingPolygon in a chain, with Actor off to the side and a second arrow from DrawablePolygon to it. Beside each class write only what it adds. Then list the six types a BlinkingPolygon object belongs to. Below that, write out the three-column comparison of superclass, abstract class and interface — can implement, can hold attributes, how many per class — and circle the row that forced Actor to be an interface.
Recap
Three sections that build a container, animate it, and then widen what it can contain.
| if you remember one thing | it is this |
|---|---|
| about polymorphism | one object, several types |
| about interfaces | a capability, not an ancestry |
| about design | depend on what you call, not on what you were given |
Want this taught 1-on-1? Alexander tutors Java — $55/session, free consultation.