Learn Java

Lesson 25Phase 3: Abstraction and design

Interface vs abstract class

When each one fits, the is-a versus can-do distinction, and how to choose with confidence.

By Updated 50 min

  • is-a vs can-do
  • design choice
  • single inheritance
  • multiple interfaces
  • trade-offs

You now know two ways to write a type that leaves blanks for other classes to fill in: an abstract class and an interface. From a distance they look alike, and people new to Java often reach for the wrong one. This lesson adds no new syntax. It gives you a test for choosing between the two, and shows what each choice costs you later, when the program grows.

By the end of this lesson
  • Say in one sentence what an abstract class is for and what an interface is for.
  • Use the “is-a” and “can-do” sentences to decide which one a design needs.
  • Name the differences that settle most cases: per-object fields and constructors, and one parent class against many interfaces.
  • Write a class that extends an abstract class and implements interfaces in the same header.
  • Explain what breaks when an interface gains a method, and add one safely with a default method.
From before
This page brings together abstract classes and interfaces, so have both fresh in your mind. It also leans on inheritance, where one class builds on another with extends, and on polymorphism, where one variable type can hold many kinds of object. Every keyword below is one you have already used.

A quick recap of both tools#

Start with each tool on its own. The difference is easiest to feel in a few sentences about each.

An abstract class is a partial class. It is a real class that you cannot build objects from directly, because some of its methods are left unfinished. It can hold fields, a constructor, and finished methods that every subclass inherits. It exists to say: all of these things are the same kind of thing, and here is the part I have already written for them. A subclass extends it and fills in the blanks.

An interface is a contract. It lists methods that a class promises to provide, usually with no bodies. It has no constructor, and it keeps no data of its own for each object. It does not ask what kind of thing you are, only whether you can do these actions. Any class, however unrelated to the others, can say implements and make that promise. An interface describes a capability.

Key idea

An abstract class answers “what kind of thing is this?” An interface answers “what can this thing do?” Most good choices follow from keeping those two questions apart.

Two verbs to hold on to
In inheritance you read extends as is-a: a Dog is-an Animal. An interface gets a different verb, can-do: a Dog can-do Soundmaker, meaning it can make a sound. These two verbs are the test you will use to decide.

The differences that matter#

A list of differences is easy to memorize and still leads to wrong choices, because it makes every row look equally important. Read the table, then the two rows under it that settle most real cases.

Abstract class
Interface
Can hold ordinary fields, such as String name. Every object gets its own copy.
Can hold only constants. Every field is automatically public, static and final: one fixed value, shared by all.
Has constructors. One runs each time a subclass object is built, reached through super(...).
Has no constructor. Nobody builds an interface object; classes promise to do what it lists.
A class can extend only one class, abstract or not.
A class can implement many interfaces, separated by commas.
Can have finished methods that every subclass inherits, and those methods can read and change the fields.
Its methods usually have no body. A default method is the exception you met: it has a body, but no per-object fields to work with.
Its methods can be public, protected, private, or have no access word at all.
The methods it promises are always public, so the class that keeps the promise must write public too.
Says the subclasses are the same kind of thing: is-a.
Says the classes share a capability: can-do.
A class joins it with extends.
A class joins it with implements.

The two rows that settle most cases

If you remember only two rows, make it these. The first is about state, the values an object keeps in its fields. Only an abstract class can carry fields, a constructor, and methods that work on those fields. If several classes really share data and the code that uses it, an interface cannot give it to them, so the answer is an abstract class.

The second is about how many. A class extends exactly one class but can implement many interfaces. If one thing needs several unrelated capabilities at once, only interfaces let it have them all.

The single-inheritance trap
Java gives each class exactly one superclass. Suppose you model a capability as an abstract class, and later a class that already extends something else needs that capability too. It cannot extend a second class, so the design is stuck. This is the most common way a beginner's design paints itself into a corner. Capabilities that many unrelated classes share almost always belong in interfaces, so that a class can take on several of them.
Quick check
Which of these is not allowed inside an interface?

The deciding test: is-a or can-do#

Here is how to turn the table into a decision you can make in seconds. For the thing you are designing, finish two sentences in plain English and see which one sounds true.

  1. 1Try the is-a sentence: “A Circle is-a Shape.” If it reads naturally, and the classes really are the same kind of thing sharing data and code, lean toward an abstract class.
  2. 2Try the can-do sentence: “A Circle can-do Drawable,” meaning it can be drawn. If that reads better, and the classes are otherwise unrelated but all support one action, lean toward an interface.
  3. 3Ask how many: does this thing need several independent capabilities at once? If yes, that settles it. Use interfaces, because a class can implement many.

One class can pass both tests, and that is fine. A Dog is-an Animal: animals share state such as a name and code such as eat, which suits an abstract class. A Dog also can-do Soundmaker, a capability that a robot or a car horn could have too, which suits an interface. A class can extend one abstract class and implement several interfaces in the same line, as you will see shortly.

Say it out loud
When you are unsure, say the sentence with both verbs. “A Paymentis-a... what?” If you cannot name a natural parent kind, stop forcing an abstract class. “A Payment can-do... be refunded?” That sounds right, so Refundable wants to be an interface.
Quick check
A game has a Knight, an Archer and a Wizard. Each has a name and health points, and all three lose health in the same way when they are hit. Only the way each one attacks is different. Which design fits best?

When the abstract class fits: shapes#

Think about drawing shapes. A circle and a rectangle really are both shapes. They share a piece of state, a name, and a finished method, describe, that reads the same for every shape. Only the way each one works out its area is different. That is the pattern an abstract class is made for: shared state, shared code, and one blank that each subclass fills. Step through it and watch super("Circle") jump into Shape's constructor.

Shapes.java
1public class Main {
2 public static void main(String[] args) {
3 Circle c = new Circle(2.0);
4 Rectangle r = new Rectangle(3.0, 4.0);
5
6 Shape[] shapes = { c, r };
7 for (int i = 0; i < shapes.length; i++) {
8 System.out.println(shapes[i].describe());
9 }
10 }
11}
12
13abstract class Shape {
14 String name;
15
16 Shape(String name) {
17 this.name = name;
18 }
19
20 abstract double area();
21
22 String describe() {
23 return name + " has area " + area();
24 }
25}
26
27class Circle extends Shape {
28 double radius;
29
30 Circle(double radius) {
31 super("Circle");
32 this.radius = radius;
33 }
34
35 double area() {
36 return 3.14 * radius * radius;
37 }
38}
39
40class Rectangle extends Shape {
41 double width;
42 double height;
43
44 Rectangle(double width, double height) {
45 super("Rectangle");
46 this.width = width;
47 this.height = height;
48 }
49
50 double area() {
51 return width * height;
52 }
53}
Prints
›Circle has area 12.56
›Rectangle has area 12.0

The output is Circle has area 12.56, then Rectangle has area 12.0. The abstract class gave you three things here. It holds state: every shape has a name. It has a constructor, which the subclasses call with super(...). And it holds finished code, describe, written once and inherited by both. An interface could not have given you the name field or the constructor.

An abstract class can have a constructor even though you can never write new Shape(...). Here each constructor prints a line. Decide what the program prints before you run it.

Predict the outputConstructors.java
1public class Main {
2 public static void main(String[] args) {
3 Shape s = new Square(3);
4 System.out.println(s.describe());
5 }
6}
7
8abstract class Shape {
9 String name;
10
11 Shape(String name) {
12 System.out.println("Shape constructor: " + name);
13 this.name = name;
14 }
15
16 abstract int area();
17
18 String describe() {
19 return name + " has area " + area();
20 }
21}
22
23class Square extends Shape {
24 int side;
25
26 Square(int side) {
27 super("Square");
28 System.out.println("Square constructor: side " + side);
29 this.side = side;
30 }
31
32 int area() {
33 return side * side;
34 }
35}

You can never build a plain Shape. What do the three lines print?

It prints Shape constructor: Square, then Square constructor: side 3, then Square has area 9. Building a Square builds the Shape part of the object first: super("Square") runs Shape's constructor before the rest of Square's. So the abstract class's constructor does run, as part of building a subclass object. An interface has nothing like it, because it has no constructor at all.

Shared code in an abstract class can also fix the order in which things happen. Below, HotDrink owns a recipe with three steps. It writes the two steps that are the same for every drink and leaves brew blank, so each drink fills in only the step that differs.

Put the lines in orderTea.java
Put the lines in order so that making a tea prints the three steps in this order: boil the water, steep the tea bag, pour it into a cup.

Tap a line, then tap where it should go. Or use the arrows.

When the interface fits: unrelated printables#

Now a different problem. A bank invoice and a holiday photo have nothing in common: one is a money document, the other is a picture. Try the is-a sentence, “an invoice is-a...”, and nothing sensible fits, because no natural parent kind covers both. They do share one capability: both can be sent to a printer. That is a can-do, so the right tool is an interface.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Printable[] things = { new Invoice(250), new Photo("sunset") };
4 for (int i = 0; i < things.length; i++) {
5 things[i].print();
6 }
7 }
8}
9
10interface Printable {
11 void print();
12}
13
14class Invoice implements Printable {
15 int total;
16
17 Invoice(int total) {
18 this.total = total;
19 }
20
21 public void print() {
22 System.out.println("Invoice total: " + total);
23 }
24}
25
26class Photo implements Printable {
27 String title;
28
29 Photo(String title) {
30 this.title = title;
31 }
32
33 public void print() {
34 System.out.println("Photo: " + title);
35 }
36}
Prints
›Invoice total: 250
›Photo: sunset

The output is Invoice total: 250, then Photo: sunset. Invoice and Photo keep their own, completely different fields, total and title, and nothing is forced on them. They are not relatives; they only keep the same promise. The for loop treats both as Printable and calls print without knowing which is which. That is polymorphism again, this time over a capability instead of a family.

Forcing the abstract version would hurt
Imagine you made Printable an abstract class instead. Invoice would have to say extends Printable, spending its one and only superclass on it. If Invoice later needed to extend a real Document base class too, it could not, because a class gets only one parent. With Printable as an interface, Invoice stays free to extend something real and still be printable.

Both tools in one design#

You do not have to pick one tool for a whole design. Often you use both: an abstract class for the family, and interfaces for the capabilities. Here a Dog is-an Animal, so Animal is an abstract class with a shared name field and a shared eat method. A Dog can-do Soundmaker too, and so can a Robot, which is no kind of animal.

Noisy.java
1public class Main {
2 public static void main(String[] args) {
3 Dog rex = new Dog("Rex");
4 Robot bot = new Robot();
5
6 rex.eat();
7
8 Soundmaker[] noisy = { rex, bot };
9 for (int i = 0; i < noisy.length; i++) {
10 noisy[i].makeSound();
11 }
12 }
13}
14
15interface Soundmaker {
16 void makeSound();
17}
18
19abstract class Animal {
20 String name;
21
22 Animal(String name) {
23 this.name = name;
24 }
25
26 void eat() {
27 System.out.println(name + " is eating");
28 }
29}
30
31class Dog extends Animal implements Soundmaker {
32 Dog(String name) {
33 super(name);
34 }
35
36 public void makeSound() {
37 System.out.println(name + " says woof");
38 }
39}
40
41class Robot implements Soundmaker {
42 public void makeSound() {
43 System.out.println("Robot says beep");
44 }
45}
Prints
›Rex is eating
›Rex says woof
›Robot says beep

The output is Rex is eating, then Rex says woof, then Robot says beep. Each tool does the job it is good at. Animal is abstract because animals are the same kind of thing and share the name field and the eat code. Soundmaker is an interface because making a sound crosses families: a Robot does it without being an animal.

Notice that Animal says nothing about sound. A goldfish is an animal that makes no sound, so a Goldfish class would extend Animal and leave Soundmaker out. The family and the capability are separate choices. Dog takes both in one header, extends Animal implements Soundmaker, which Java allows because only one of the two is a class.

Key idea

Use an abstract class for the kind of thing an object is, and interfaces for what it can do. A class extends one kind and can implement many capabilities.

Quick check
Dog is an Animal (an abstract class) and can make a sound (the Soundmaker interface). Which first line of the class compiles?
ExerciseMoreNoise.java
Add two more noise makers. A Cat is an animal that says meow, so it belongs in the Animal family and keeps the Soundmaker promise. A Doorbell is not an animal at all: it only keeps the promise, and prints Ding dong. Write both classes, then add a cat called Tom and a doorbell to the end of the array.

When the design grows#

A design is never finished. Months after you write Printable, someone asks for a print preview. The two tools react to a new method in very different ways.

A new method with no body breaks every class that implements it

Add one line to the interface, void preview();, and every class that implements Printable has broken its promise, because none of them has a previewmethod yet. The comments show javac's messages:

Printable.java (does not compile)
read along
1interface Printable {
2 void print();
3 void preview(); // the new line
4}
5
6// error: Invoice is not abstract and does not override abstract method preview() in Printable
7class Invoice implements Printable {
8 int total;
9
10 Invoice(int total) {
11 this.total = total;
12 }
13
14 public void print() {
15 System.out.println("Invoice total: " + total);
16 }
17}
18
19// error: Photo is not abstract and does not override abstract method preview() in Printable
20class Photo implements Printable {
21 String title;
22
23 Photo(String title) {
24 this.title = title;
25 }
26
27 public void print() {
28 System.out.println("Photo: " + title);
29 }
30}

In your own program you would add preview to both classes and move on. A library cannot do that: thousands of programs it has never seen implement its interfaces, and all of them would stop compiling. An abstract class does not have this problem. Add a finished method to the base class and every subclass inherits it, with nothing to fix.

Default methods let an interface grow

This is the reason default methods exist. For Java 8, the Java team wanted to give the collection interfaces new methods, such as sort on List and forEach on every collection. Added as plain promises, they would have broken every list class anyone had ever written. As default methods they came with a body, so existing classes inherited them and kept compiling. You can do the same with preview:

Preview.java
1public class Main {
2 public static void main(String[] args) {
3 Printable[] things = { new Invoice(250), new Photo("sunset") };
4 for (Printable p : things) {
5 p.preview();
6 }
7 }
8}
9
10interface Printable {
11 void print();
12
13 default void preview() {
14 System.out.print("Preview: ");
15 print();
16 }
17}
18
19class Invoice implements Printable {
20 int total;
21
22 Invoice(int total) {
23 this.total = total;
24 }
25
26 public void print() {
27 System.out.println("Invoice total: " + total);
28 }
29}
30
31class Photo implements Printable {
32 String title;
33
34 Photo(String title) {
35 this.title = title;
36 }
37
38 public void print() {
39 System.out.println("Photo: " + title);
40 }
41}
Prints
›Preview: Invoice total: 250
›Preview: Photo: sunset

It prints Preview: Invoice total: 250 and Preview: Photo: sunset, and neither Invoice nor Photo changed. The default method is built from print(), a method the interface already promises. That is all a default method can build on: the interface's other methods, never fields that belong to one object.

A class may still write its own version of a default method. Photo would rather show a small thumbnail than the whole photo. Decide what the loop prints now.

Predict the outputThumbnail.java
1public class Main {
2 public static void main(String[] args) {
3 Printable[] things = { new Invoice(250), new Photo("sunset") };
4 for (Printable p : things) {
5 p.preview();
6 }
7 }
8}
9
10interface Printable {
11 void print();
12
13 default void preview() {
14 System.out.print("Preview: ");
15 print();
16 }
17}
18
19class Invoice implements Printable {
20 int total;
21
22 Invoice(int total) {
23 this.total = total;
24 }
25
26 public void print() {
27 System.out.println("Invoice total: " + total);
28 }
29}
30
31class Photo implements Printable {
32 String title;
33
34 Photo(String title) {
35 this.title = title;
36 }
37
38 public void print() {
39 System.out.println("Photo: " + title);
40 }
41
42 public void preview() {
43 System.out.println("Thumbnail of " + title);
44 }
45}

Photo now writes its own preview. What does the loop print?

It prints Preview: Invoice total: 250, then Thumbnail of sunset. Invoice has no preview of its own, so it uses the interface's default. Photo's own method replaces the default, the same way an overriding method replaced an inherited one in method overriding. A method written in the class always wins over a default from an interface.

Key idea

Adding a method with no body to an interface breaks every class that implements it. A default method adds behavior without breaking anything, built from the methods the interface already promises.

A short checklist you can use#

When you next have to choose, go down this list from the top. The first clear yes usually decides it.

  • Do the classes need to share real fields, or methods that work on those fields? Then it must be an abstract class. Interfaces cannot carry per-object state.
  • Does one thing need several independent capabilities at once? Then use interfaces, because a class can implement many but extend only one.
  • Read the sentences “X is-a Y” and “X can-do Y”. Whichever sounds natural points at the tool: is-a leans toward an abstract class, can-do toward an interface.
  • Are the classes otherwise unrelated, sharing only one action? That is a classic interface.
  • Still torn? Prefer the interface. It never spends a class's one superclass, and if real shared state turns up later you can still add an abstract class that implements the interface, the way AbstractList does for List.
Where interfaces turn up next
Interfaces run through the rest of the course. In enums and lambdas you meet interfaces with a single method, which let you pass a piece of behavior around like a value. Sealed typeslet an interface name the only classes allowed to implement it. And Java's own library is full of capability interfaces: comparing and sorting introduces Comparable (can be put in order), and the Collections Framework shows that List, Set and Map are interfaces with several classes behind each one.

Try it

Answer each one in your head or on paper first, then open the answer.

1
In one sentence each, finish: an abstract class is best when ___, and an interface is best when ___.
2
You are modeling Bird, Plane and Drone, which are unrelated but can all fly(). Abstract class or interface? Why?
3
In the Animal and Soundmaker example, why was Animal an abstract class but Soundmaker an interface? Name the deciding factor for each.
4
True or false: a class can extends two abstract classes at once. Explain.
5
A teammate makes Printable an abstract class. Later Invoice needs to extend a real Document base class as well. What goes wrong, and what is the fix?
6
A library adds void preview(); with no body to an interface that hundreds of classes implement. What happens to those classes, and how could the library have added preview without breaking them?

Practice

Work through these on your own. Each one builds on this page.

1
On paper, design a payments system. CardPayment and CashPayment are the same kind of thing and share an amount field and a finished summary() method. Separately, some payments can be refunded and some cannot. Decide which idea is an abstract class and which is an interface, and write one sentence justifying each with the is-a or can-do test.
2
Open the shapes example in the playground and add a third shape, Square, that extends Shape and works out its own area. Check that the inherited describe method works for it without any change. In a comment, note which two things the abstract Shape gave Square for free.
3
Take the Animal and Soundmaker program and add a second capability interface, Trainable, with a method doTrick(). Make Dog implement both Soundmaker and Trainable while still extending Animal, and let Robot implement only Soundmaker. In one sentence, explain why Dog can take on two capabilities but only one superclass.
4
Give Soundmaker a default method makeSoundTwice() that calls makeSound() two times. Check that Dog and Robot compile without any change. Then give Robot its own makeSoundTwice() that prints Robot says beep beep on one line, and predict which version runs for each object before you run it.
5
Write a short paragraph in your own words on this advice: “When you are unsure, prefer the interface.” What freedom does choosing the interface protect, and what can you still add later if real shared state turns out to be needed?

Further reading#

Spotted a mistake, or a part that did not make sense? Report it by email