Interface vs abstract class
When each one fits, the is-a versus can-do distinction, and how to choose with confidence.
Finished reading?
Mark this session so you can track where you are.
When each one fits, the is-a versus can-do distinction, and how to choose with confidence.
Finished reading?
Mark this session so you can track where you are.
You now know two ways to write a class that leaves blanks for others to fill in: an abstract class, and an interface. They look similar from a distance, and beginners reach for the wrong one all the time. This session is not about new syntax. It is about one question: when you sit down to design something, which of the two do you pick, and how do you know you picked right?
Before we compare, let us make sure both pictures are sharp. They solve different problems, and the difference is easiest to feel with a single sentence for each.
An abstract classis a partial base. It is a real class that you simply cannot build objects from directly, because it left some methods unfinished. It can hold fields, it can hold a constructor, and it can hold finished methods that every subclass inherits. Its whole reason to exist is to say “all of these things are the same kindof thing, and here is the shared part I have already written for them.” A subclass extends it and fills in the blanks.
An interface is a pure contract. It lists method signatures with no bodies and, in its classic form, no fields and no constructor. It does not care what kind of thing you are. It only asks: can you do these actions? Any class at all, however unrelated, can implementsit and promise “yes, I can do that.” It describes a capability, not a family.
An abstract class answers “what kind of thing is this?” An interface answers “what can this thing do?” Almost every right choice flows from getting that one distinction clear.
Dog is-an Animal. With interfaces we use a different verb: a class can-do the interface. A Dog can-do Soundmaker. Hold onto both verbs. They are the test you will use to decide.People memorize lists of differences and still choose wrong, because they treat every row as equally important. They are not. A few of these rows decide most real cases, so read the table, then we will pull out the two rows that do the heavy lifting.
String name that subclasses inherit.constructor, run by a subclass through super(...).extends.implements.If you remember only two rows, remember these. First, shared state and code: only an abstract class can carry fields and already-written methods that subclasses inherit. If several classes genuinely share data and behavior, an interface cannot give it to them, so an abstract class is your answer. Second, how many you can have: a class extends exactly one abstract class but can implement many interfaces. If a thing needs to wear several unrelated capabilities at once, only interfaces let it.
Here is the move that turns the table above 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.
Circle is-a Shape.” If that reads naturally, and the members really are the same kind of thing sharing data and code, lean toward an abstract class.Circle can-do Drawable.” If that reads better, and the members are otherwise unrelated but all happen to support one action, lean toward an interface.The trick is that the same word can pass both tests, and that is fine. A Dog is-an Animal (abstract class, because animals share state like name and shared behavior like eat), and a Dog can-do Soundmaker (interface, a capability a robot or a car horn could also have). The two are not rivals. A class can extend one abstract base and implement several interfaces in the same breath. We will see exactly that in a moment.
Payment is-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.Consider drawing shapes. A circle and a rectangle are both, genuinely, shapes. They share a piece of state, a name, and they share a finished behavior, a describe method, that reads the same for every shape. What differs is only how each one computes its area. That is the textbook signature of an abstract class: real shared state, real shared code, and one blank that each subclass fills. Read it, then run it.
public class Main { public static void main(String[] args) { Circle c = new Circle(2.0); Rectangle r = new Rectangle(3.0, 4.0); Shape[] shapes = { c, r }; for (int i = 0; i < shapes.length; i++) { System.out.println(shapes[i].describe()); } }} abstract class Shape { String name; Shape(String name) { this.name = name; } abstract double area(); String describe() { return name + " has area " + area(); }} class Circle extends Shape { double radius; Circle(double radius) { super("Circle"); this.radius = radius; } double area() { return 3.14 * radius * radius; }} class Rectangle extends Shape { double width; double height; Rectangle(double width, double height) { super("Rectangle"); this.width = width; this.height = height; } double area() { return width * height; }}Circle has area 12.56 Rectangle has area 12.0
The output is Circle has area 12.56 then Rectangle has area 12.0. Notice three things the abstract class earned you. It holds state: every shape has a name. It has a constructor the subclasses call with super(...). And it ships finished code, the describe method, written once and inherited by both. An interface could not have given you the name field or the constructor. This is an abstract class through and through.
Now a different problem. A bank invoice and a holiday photo have nothing in common. One is a money document, the other is an image. There is no honest parent kind that covers both, so an abstract class would be a lie: an Invoice is-not-a Photo, and neither is-a... what, exactly? But they do share one capability: both can be sent to a printer. That is a can-do, not an is-a, so the right tool is an interface.
public class Main { public static void main(String[] args) { Printable[] things = { new Invoice(250), new Photo("sunset") }; for (int i = 0; i < things.length; i++) { things[i].print(); } }} interface Printable { void print();} class Invoice implements Printable { int total; Invoice(int total) { this.total = total; } public void print() { System.out.println("Invoice total: " + total); }} class Photo implements Printable { String title; Photo(String title) { this.title = title; } public void print() { System.out.println("Photo: " + title); }}Invoice total: 250 Photo: sunset
The output is Invoice total: 250 then Photo: sunset. Look at what the interface let you do that an abstract class never could have. Invoice and Photo keep their own completely different fields, total and title, with no shared state forced on them. They are not relatives. They only sign the same contract. And the for loop treats both as a Printable, calling print without knowing or caring which is which. That is polymorphism again, this time over a capability rather than a family.
Printable class instead. Now Invoice must extends Printable, spending its one and only superclass slot on it. If Invoice later needed to extend a real Document base too, it could not: a class gets only one parent. Because Printable is an interface, Invoice stays free to extend something real and still be printable. Capabilities want to be interfaces for exactly this reason.The most important thing to unlearn is the idea that you must choose one tool for the whole design. You often use both at once: an abstract class for the family, interfaces for the capabilities. Here a Dog is-an Animal (so Animal is an abstract class with shared state and the shared eat method) and can-do Soundmaker (an interface a Robot, which is no kind of animal at all, also satisfies).
public class Main { public static void main(String[] args) { Dog d = new Dog("Rex"); Robot bot = new Robot(); d.eat(); Soundmaker[] noisy = { d, bot }; for (int i = 0; i < noisy.length; i++) { noisy[i].makeSound(); } }} interface Soundmaker { void makeSound();} abstract class Animal { String name; Animal(String name) { this.name = name; } void eat() { System.out.println(name + " is eating"); } abstract void makeSound();} class Dog extends Animal implements Soundmaker { Dog(String name) { super(name); } public void makeSound() { System.out.println(name + " says woof"); }} class Robot implements Soundmaker { public void makeSound() { System.out.println("Robot says beep"); }}Rex is eating Rex says woof Robot says beep
The output is Rex is eating, then Rex says woof, then Robot says beep. Trace the design decisions and you can see each tool doing the job it is good at. Animal is abstract because dogs and the other animals truly are the same kind of thing and share the name field and the eat code. Soundmaker is an interface because making a sound is a capability that crosses unrelated families: a Robot can do it without being an animal in any sense. And Dog happily does both, extends Animal implements Soundmaker, which is only legal because one of them is an interface.
Use an abstract class for the kind a thing is. Use interfaces for the capabilities it has. A class extends one kind and can wear many capabilities. They are partners, not competitors.
When you next have to choose, walk this list top to bottom. The first clear “yes” usually decides it.
Try each one yourself first, then open the answer.
Bird, Plane, and Drone, which are unrelated but all fly(). Abstract class or interface? Why?Animal and Soundmaker example, why was Animal an abstract class but Soundmaker an interface? Name the deciding factor for each.extends two abstract classes at once. Explain.Printable an abstract class. Later Invoice needs to extend a real Document base as well. What goes wrong, and what is the fix?Take these away. They continue exactly what we just did.
CardPayment and CashPayment, which are the same kind and share an amount field and a finished summary() method. Separately, some payments can-do Refundable. 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.Square, that extends Shape and computes its own area. Confirm the inherited describe method works for it without you rewriting it. In a comment, note which two things the abstract Shape gave Square for free.Animal/Soundmaker program and add a second capability interface, Trainable, with a method doTrick(). Make Dog implement both Soundmaker and Trainable while still extending Animal. Have Robot implement only Soundmaker. In one sentence, explain why Dog can carry two capabilities plus a superclass but only the one superclass.