Interfaces
A pure contract of behavior, implementing one, and why a class can satisfy many at once.
Finished reading?
Mark this session so you can track where you are.
A pure contract of behavior, implementing one, and why a class can satisfy many at once.
Finished reading?
Mark this session so you can track where you are.
You can already make one class stand in for many shapes of object, and you can force a parent to hand down behavior its children must respect. Here is the next move: describing a capability on its own, separate from any particular family of classes. Not “this thing is an animal”, but “this thing, whatever it is, can be drawn”. That promise is called an interface, and it is one of the most useful ideas in all of Java.
interface is in one plain sentence: a contract of behavior with no state.implements an interface and provides every method it promised.Suppose we are building a small drawing program. It paints shapes on a screen, but it also paints other things: a button, a chart, a piece of text. The one thing all of these have in common is that the program can ask each of them to draw itself. They share a capability, drawing, but they do not share a family. A button is not a kind of circle. A chart is not a kind of text. There is no sensible parent class that a circle, a button, and a chart all descend from.
With inheritance alone you are stuck. Inheritance says “is a kind of”, and these things are not kinds of each other. What we want to say is something looser and more honest: “each of these can be drawn, however differently they go about it”. We need a way to name a capability by itself, with no family attached.
Inheritance answers “what is this thing?” An interface answers a different question: “what can this thing do?” Those are not the same question, and many real designs need the second one.
An interface is a list of method signatures, the names, parameters, and return types, with no bodies and no fields holding data. It does not say how anything is done. It only says what must be possible. Read this one out loud:
public class Main { public static void main(String[] args) { System.out.println("An interface defines what, not how."); }} interface Drawable { void draw();}An interface defines what, not how.
That interface, Drawable, says exactly one thing: any class that claims to be drawable must provide a method draw() that takes nothing and returns nothing. There is no body after draw(), just a semicolon. The interface is not telling anyone how to draw. It is only publishing the promise that drawing will be possible. The little program above does nothing with Drawable yet; it just proves the interface compiles. We give it teeth in a moment.
; instead of { ... } is a promise of a method, not a method. The class that implements the interface is where the real body lives.An interface is a promise. It names what a class must be able to do, and says nothing about how the class does it.
A promise is only worth something when someone keeps it. A class keeps an interface's promise with the keyword implements. Where inheritance used extendsto say “is a kind of”, an interface uses implementsto say “can do”. When a class says it implements Drawable, the compiler forces it to provide a real draw() with a body, or it will not compile at all. Here is a Circle that keeps the promise.
public class Main { public static void main(String[] args) { Circle c = new Circle(); c.draw(); }} interface Drawable { void draw();} class Circle implements Drawable { public void draw() { System.out.println("Drawing a circle"); }}Drawing a circle
Trace it in your head: we build a Circle, call c.draw(), and it prints Drawing a circle. The Circle declared implements Drawable, so it had to define draw(), and it did. Notice the method is marked public. Interface methods are public to everyone by nature, since the whole point is to advertise a capability, so the class that provides them must make them public too.
Circle said implements Drawable but never wrote a draw()method, Java would stop you before the program ran, with an error like “Circle is not abstract and does not override abstract method draw()”. This is the safety the contract buys you. The moment a class claims a capability, the compiler holds it to that claim. You cannot accidentally ship a drawable thing that cannot draw.Let us add a second, unrelated class. A Square is not a kind of Circle and shares no parent with it, yet both can be drawn. Each one fills in draw() its own way.
public class Main { public static void main(String[] args) { Circle c = new Circle(); Square s = new Square(); c.draw(); s.draw(); }} interface Drawable { void draw();} class Circle implements Drawable { public void draw() { System.out.println("Drawing a circle"); }} class Square implements Drawable { public void draw() { System.out.println("Drawing a square"); }}Drawing a circle Drawing a square
This prints Drawing a circle then Drawing a square. Two classes with nothing else in common both keep the same promise, in their own way. That shared promise is about to let us treat them as one kind of thing, even though they share no family.
Here is where the contract pays off. Because both Circle and Square implement Drawable, a variable typed Drawable can hold either one. This is the same upcasting you saw with polymorphism, except the general type is now an interface rather than a parent class. The program can hold a thing by its capability and forget what it actually is.
public class Main { public static void main(String[] args) { Drawable d = new Circle(); d.draw(); d = new Square(); d.draw(); }} interface Drawable { void draw();} class Circle implements Drawable { public void draw() { System.out.println("Drawing a circle"); }} class Square implements Drawable { public void draw() { System.out.println("Drawing a square"); }}Drawing a circle Drawing a square
Step through it. The variable d is typed Drawable. First it holds a real Circle, so d.draw() prints Drawing a circle. Then the very same variable is pointed at a real Square, and now d.draw() prints Drawing a square. The declared type Drawable decided what we were allowed to call, just draw(), and the real object decided what actually ran. That is dynamic dispatch again, working exactly as it did for classes.
Now put the whole idea in one loop. An array typed Drawable[] can hold a circle and a square side by side, and one line draws them all.
public class Main { public static void main(String[] args) { Drawable[] shapes = { new Circle(), new Square(), new Circle() }; for (int i = 0; i < shapes.length; i++) { shapes[i].draw(); } }} interface Drawable { void draw();} class Circle implements Drawable { public void draw() { System.out.println("Drawing a circle"); }} class Square implements Drawable { public void draw() { System.out.println("Drawing a square"); }}Drawing a circle Drawing a square Drawing a circle
The output is Drawing a circle, Drawing a square, Drawing a circle. The loop never mentions Circle or Square. It speaks only to the capability, Drawable, and each object answers in its own way. Add a Triangle that implements Drawable, drop a new Triangle() into the array, and the loop draws it too, untouched. The interface is the common language the loop and the objects agree to speak.
Code written against an interface does not care what the real objects are, only that they kept the promise. You add new kinds by writing new classes, never by editing the code that uses them.
draw method into a list and call x.draw()on each; it worked as long as the method existed. Python checked at the moment of the call, the famous “if it can draw, treat it as drawable” style. Java asks you to write the promise down as an interface first, so the compiler can confirm every drawable thing really can draw before the program runs. Same instinct, made explicit and checked early.Here is where interfaces pull decisively ahead of inheritance. A class may extend only one parent class. Java allows just one. But a class may implement as manyinterfaces as it likes, listing them with commas. That makes sense once you remember what each one means. A thing has exactly one identity, one “what it is”, but it can have many capabilities, many “what it can do”.
Picture an on-screen button. It can be drawn, and it can be clicked. Those are two separate capabilities. We write one interface for each, and a Button class promises both.
public class Main { public static void main(String[] args) { Button b = new Button(); b.draw(); b.click(); }} interface Drawable { void draw();} interface Clickable { void click();} class Button implements Drawable, Clickable { public void draw() { System.out.println("Drawing a button"); } public void click() { System.out.println("Button clicked"); }}Drawing a button Button clicked
The output is Drawing a button then Button clicked. The Button said implements Drawable, Clickable, so the compiler required both draw() and click(), and we provided both. One object, two contracts kept. A plain Circle might implement only Drawable; a checkbox might implement both; a background sound might implement only some Playable interface. Each class picks up exactly the capabilities it actually has.
Dog is an Animal).Two smaller features round out the picture. You will meet them often, so it is worth a quick, honest look, but you do not need to lean on them yet.
An interface can hold a named constant, a value that never changes, shared by everyone who implements it. A field written in an interface is automatically a shared constant: public (visible everywhere), static (one copy for the whole interface, not one per object), and final(set once and never reassigned). You write it once and read it through the interface name. It is a tidy home for a fixed number that several classes agree on.
public class Main { public static void main(String[] args) { Square s = new Square(5); s.draw(); }} interface Polygon { int SQUARE_SIDES = 4;} class Square implements Polygon { int side; Square(int side) { this.side = side; } void draw() { int perimeter = Polygon.SQUARE_SIDES * side; System.out.println("A square with side " + side + " has perimeter " + perimeter); }}A square with side 5 has perimeter 20
This prints A square with side 5 has perimeter 20. The constant SQUARE_SIDES lives in the interface, is read as Polygon.SQUARE_SIDES, and cannot be reassigned because it is final, meaning its value is fixed once and any attempt to change it is refused. Any class that implements Polygon can rely on that shared 4.
public, static, and final have a fuller life of their own beyond interfaces, especially static (members that belong to the type, not to any object) and final (values that cannot change). We give them a session of their own in static and final. Here you only need the one-line meaning above; the deeper treatment comes later.For most of its life an interface could only hold body-less promises. Modern Java relaxed that a little: an interface may now provide a default method, a method with a real body, marked with the keyword default. Any class that implements the interface gets that method for free and may use it as-is or replace it. It is a way to add a shared, optional behavior to a contract without forcing every existing class to write it. Read this one along; it shows the shape.
public class Main { public static void main(String[] args) { Circle c = new Circle(); c.draw(); c.describe(); }} interface Drawable { void draw(); default void describe() { System.out.println("Describing: I am a drawable thing."); }} class Circle implements Drawable { public void draw() { System.out.println("Drawing a circle"); }}Drawing a circle Describing: I am a drawable thing.
The Circle only wrote draw(), yet it can call describe() because the interface provided a default body. Keep this light for now. The lesson is just that an interface is mostly pure promises, and default methods are a careful, modern exception that lets a contract carry a small amount of shared behavior.
You now know two tools that both force a class to provide certain methods: the abstract class you met last time, and the interface you met today. They overlap, and choosing between them is a real skill. Here is only the seed of the comparison, enough to feel the difference. The full decision framework is the whole of the next session.
Try each one yourself first, then open the answer.
interface, and how is the question it answers different from the question inheritance answers?class Circle implements Drawable, where Drawable has one method void draw();, but the author forgot to write a draw() body in Circle. When does this fail, compile time or run time, and why?Drawable d = new Square(); where both Circle and Square implement Drawable, what does d.draw() print, and what decided it?Button class is implements Drawable, Clickable. List exactly which methods the compiler forces it to provide, and what happens to those methods' visibility.Take these away. They continue exactly what we just did.
Drawable[] loop into the playground. Add a third class, Triangle, that implements Drawable and prints Drawing a triangle. Drop a new Triangle() into the array and run it. Confirm the loop draws the triangle without you changing the loop at all.Resizable, with one method void resize(int factor);. Make a class Photo that implements both Drawable and Resizable. Print something sensible from each method. Then call both methods on a Photo object and run it.Drawable[] can hold a circle, a square, and a button at the same time, even though those three classes share no parent. Use the word “capability” at least once.int MAX_SHAPES = 100; inside an interface called Canvas, and from main print it using Canvas.MAX_SHAPES. Then try to reassign it with Canvas.MAX_SHAPES = 50; and read the compiler error. Write down, in one sentence, why the reassignment is refused.