Kestrel Exams kestrel logo KESTREL EXAMSPRACTICE TO PASS
Java12 min readCode verified

Abstract classes and interfaces: when to use which

Abstract classes and interfaces both let you write code against a general idea and leave the details to other classes. This guide covers abstract methods, what the compiler says when a promise is not kept, how interfaces let one class take on several roles, the side-by-side comparison, when to choose which, and how each is drawn in UML. Every listing was compiled and run on OpenJDK 21.

Practice this topic in the free app →All Java guides

A half-finished form#

Think of a job application form. The company has printed most of it already: name, address, the signature line. But one box says “Describe why you want this job” and it is blank. The form is useless until each applicant fills that box in, and nobody would hand in the blank master copy as their application.

An abstract class is that form. Most of the class is written, but some methods are left blank on purpose, and every subclass must fill them in. Those blank methods are abstract methods: a heading with no body, ending in a semicolon. And just as nobody submits the blank master, Java will not let you create an object of an abstract class.

Abstract methods and abstract classes#

abstract class Shape {
    private String name;

    public Shape(String name) {
        this.name = name;
    }

    public abstract double area();

    public String describe() {
        return name + " with area " + area();
    }
}

class Circle extends Shape {
    private double r;

    public Circle(double r) {
        super("circle");
        this.r = r;
    }

    @Override
    public double area() {
        return Math.round(Math.PI * r * r * 100) / 100.0;
    }
}

class Rect extends Shape {
    private double w, h;

    public Rect(double w, double h) {
        super("rectangle");
        this.w = w;
        this.h = h;
    }

    @Override
    public double area() {
        return w * h;
    }
}

public class Shapes {
    public static void main(String[] args) {
        Shape[] shapes = { new Circle(2), new Rect(3, 4) };
        for (Shape s : shapes) {
            System.out.println(s.describe());
        }
    }
}
circle with area 12.57
rectangle with area 12.0

Three things to notice:

  • area() in Shape has no body, just a semicolon, and is marked abstract. Any class holding an abstract method must itself be marked abstract.
  • describe() is an ordinary, finished method in the same abstract class. It even calls area(), trusting that every real shape will supply one. An abstract class can also have fields and constructors, and Shape uses its constructor to store the name.
  • The array is declared as Shape[], and polymorphism does the rest: each object runs its own area().

Try to create the blank master copy and the compiler stops you:

abstract class Shape {
    public abstract double area();
}

public class NoNew {
    public static void main(String[] args) {
        Shape s = new Shape();
    }
}
NoNew.java:7: error: Shape is abstract; cannot be instantiated
        Shape s = new Shape();
                  ^
1 error

And a subclass that forgets to fill in the blank gets a precise complaint. The message tells you exactly which method is missing:

abstract class Shape {
    public abstract double area();
}

class Triangle extends Shape {
    private double base, height;
}

public class Forgot {
    public static void main(String[] args) {
        System.out.println("never gets here");
    }
}
Forgot.java:5: error: Triangle is not abstract and does not override abstract method area() in Shape
class Triangle extends Shape {
^
1 error

The only ways out are to write the missing area(), or to mark Triangle abstract too, which passes the job further down the family.

When to make a class abstract#

Make a class abstract when both of these are true:

  1. It names a category, not a real thing. You can draw a circle or a square, but you cannot draw “a shape” that is no particular shape. Likewise “an employee” who is no particular kind of employee, or “an account” that is no particular kind of account.
  2. Every subclass must provide some behaviour, but each does it differently. Every shape has an area; each computes it its own way.

If the parent can give a sensible default for the method, it does not need to be abstract: write the default and let subclasses override it. Use abstract when there is no honest default, and forgetting to override would be a bug.

Interfaces: a promise any class can make#

Phones, headphones and electric toothbrushes have nothing else in common, but they can all promise to fit a USB-C charger. That promise is a standard. Any device can meet it, whatever family the device belongs to.

An interface is that standard in Java: a named list of method headings. A class signs up with the keyword implements, and must then write every method on the list.

interface Chargeable {
    int batteryLevel();
    void charge(int minutes);
}

class Device {
    private String model;

    public Device(String model) {
        this.model = model;
    }

    public String getModel() {
        return model;
    }
}

class Phone extends Device implements Chargeable {
    private int battery = 20;

    public Phone(String model) {
        super(model);
    }

    public int batteryLevel() {
        return battery;
    }

    public void charge(int minutes) {
        battery = Math.min(100, battery + minutes);
    }
}

class Toothbrush implements Chargeable {
    private int battery = 50;

    public int batteryLevel() {
        return battery;
    }

    public void charge(int minutes) {
        battery = Math.min(100, battery + minutes / 2);
    }
}

public class Iface {
    public static void main(String[] args) {
        Phone p = new Phone("Pixel");
        Chargeable[] plugs = { p, new Toothbrush() };
        for (Chargeable c : plugs) {
            c.charge(60);
            System.out.println(c.batteryLevel() + "%");
        }
        System.out.println(p.getModel() + " is at " + p.batteryLevel() + "%");
    }
}
80%
80%
Pixel is at 80%

The rules that make interfaces different from classes:

  • Methods in an interface are automatically public and abstract; you do not write those words. (Since Java 8 an interface may also contain default and static methods with a body, and since Java 9 private helper methods, but the heart of an interface is the list of promises.)
  • Any field in an interface is automatically public static final: a shared constant, not per-object data.
  • You cannot create an object of an interface, but you can use it as a variable or array type, as Chargeable[] does above. That is polymorphism again.
  • A class extends one class but can implement many interfaces, separated by commas. Phone extends Device and is also Chargeable.

Implementing class methods must be public. Leave the word off and you get the same “weaker access privileges” error as in the Object guide.

A real interface you will use: Comparable#

The Java library is full of interfaces. The one your course meets first is Comparable, which promises one method, compareTo. It follows the same rule as String’s compareTo: negative means “I come first”, zero means equal, positive means “I come after”. Keep that promise and Arrays.sort can sort your objects:

import java.util.Arrays;

class Student implements Comparable<Student> {
    private String name;
    private double gpa;

    public Student(String name, double gpa) {
        this.name = name;
        this.gpa = gpa;
    }

    public int compareTo(Student other) {
        return Double.compare(other.gpa, gpa);
    }

    @Override
    public String toString() {
        return name + " " + gpa;
    }
}

public class Sort {
    public static void main(String[] args) {
        Student[] cls = {
            new Student("Ana", 3.2),
            new Student("Ben", 3.9),
            new Student("Cy", 3.5)
        };
        Arrays.sort(cls);
        System.out.println(Arrays.toString(cls));
    }
}
[Ben 3.9, Cy 3.5, Ana 3.2]

Double.compare(x, y) does the subtraction-style comparison for two double values. Passing other.gpa first flips the order, which is why the highest GPA comes out first; swap the arguments for lowest first. Arrays.sort was written years before Student existed. It works because it only relies on the promise: anything Comparable has a compareTo. The <Student> after the interface name is generics, covered later in this series; it says “comparable to other Students”.

Abstract class or interface?#

Abstract classInterface
Keyword to use itextendsimplements
How many per classoneas many as you like
Can you create an object of it?nono
Constructorsyesno
Fieldsany kind, including private per-object dataonly public static final constants
Methods with bodiesyes, as many as you likeonly default, static and private ones
Relationship it expressesis a (a circle is a shape)can do (a phone can be charged)

When to use an interface: when unrelated classes need to share an ability, or when a class needs more than one role. Phones and toothbrushes share no parent but can both be Chargeable.

When to use an abstract class: when closely related classes share real code and data, such as a name field, a constructor or a finished describe() method, and you only want to leave a few methods blank.

A quick exam test: if the sentence is “X is a kind of Y”, Y is probably a class (abstract if Y is only a category). If the sentence is “X can do Z”, Z is probably an interface, and its name often ends in -able.

Drawing them in UML#

UML marks abstract things in italics. Because italics are hard to write by hand, you may also see {abstract} after the name. An interface gets the label «interface» above its name. The arrows:

  • extends (class to class): solid line, hollow triangle at the parent.
  • implements (class to interface): dashed line, hollow triangle at the interface.
+------------------------------+      +------------------------------+
|      Shape  {abstract}       |      |        «interface»           |
+------------------------------+      |         Chargeable           |
| - name : String              |      +------------------------------+
+------------------------------+      | + batteryLevel() : int       |
| + area() : double {abstract} |      | + charge(minutes : int)      |
| + describe() : String        |      +------------------------------+
+------------------------------+      
               ^                                     ^
               |                                     :
               |  solid line                         :  dashed line
+------------------------------+      +------------------------------+
|           Circle             |      |            Phone             |
+------------------------------+      +------------------------------+
| - radius : double            |      | - battery : int              |
+------------------------------+      +------------------------------+
| + area() : double            |      | + batteryLevel() : int       |
+------------------------------+      | + charge(minutes : int)      |
                                      +------------------------------+

Notice Circle lists area() in plain type: it is the finished version. The abstract heading appears only in the parent.

One sentence to carry into the exam

An abstract class is a half-finished parent you extend (one only, is-a); an interface is a list of promises you implement (as many as you like, can-do). You can create an object of neither, and every abstract method must be written by the first non-abstract class below it.

Where these go wrong#

  • Giving an abstract method a body, or a normal method no body. Abstract methods end in a semicolon; every other method needs braces.
  • Writing new Shape() for an abstract class or new Chargeable() for an interface. Neither can be instantiated. Create a concrete subclass or implementing class instead.
  • Forgetting one abstract method in a subclass. The class must be marked abstract or the compiler names the missing method.
  • Leaving public off an implemented interface method. Interface methods are public, and an implementation cannot be weaker.
  • Writing extends for an interface or implements for a class. Classes are extended; interfaces are implemented. (One interface can extend another interface.)
  • Expecting per-object fields in an interface. Every interface field is a public static final constant.
  • Mixing up the UML arrows. Solid line for extends, dashed line for implements, both with a hollow triangle at the general end.

Practice#

Answer each one on paper before you look at the solutions. Every answer that involves running code was checked by compiling and running it.

  1. Which of these lines compile, given abstract class Animal and class Cow extends Animal (which writes every abstract method)? Animal a = new Animal();   Animal b = new Cow();   Animal[] herd = new Animal[10];
  2. Animal declares public abstract String sound(); and a finished speak() that returns "The animal says " + sound(). Write the smallest Cow class that compiles, and say what new Cow().speak() returns.
  3. A class Robot already extends Machine. It also needs to be Rechargeable and Programmable. Should those two be abstract classes or interfaces? Why?
  4. Write an interface Playable with one method String play(), two classes that implement it, and a loop over a Playable[] that calls play() on each.
  5. Give three differences between an abstract class and an interface.
  6. In UML, how do you tell an abstract class from an ordinary class, and how do you draw “Phone implements Chargeable”?

Solutions#

  1. The second and third. You cannot create an Animal object (Animal is abstract; cannot be instantiated), but you can declare Animal variables and arrays, which is the whole point. new Animal[10] creates an array of ten empty slots, not ten animals, so it is allowed.
  2. Cow only has to fill in the blank; it inherits speak():
    abstract class Animal {
        public abstract String sound();
    
        public String speak() {
            return "The animal says " + sound();
        }
    }
    
    class Cow extends Animal {
        @Override
        public String sound() {
            return "moo";
        }
    }
    
    public class QFix {
        public static void main(String[] args) {
            Animal a = new Cow();
            System.out.println(a.speak());
        }
    }
    The animal says moo
  3. Interfaces. A class can extend only one class, and Robot has used its one on Machine. It can implement any number of interfaces: class Robot extends Machine implements Rechargeable, Programmable. They also describe abilities (can-do), which is what interfaces are for.
  4. The array type is the interface, and each object runs its own play():
    interface Playable {
        String play();
    }
    
    class Song implements Playable {
        public String play() {
            return "playing a song";
        }
    }
    
    class Podcast implements Playable {
        public String play() {
            return "playing a podcast";
        }
    }
    
    public class QPlay {
        public static void main(String[] args) {
            Playable[] queue = { new Song(), new Podcast() };
            for (Playable p : queue) {
                System.out.println(p.play());
            }
        }
    }
    playing a song
    playing a podcast
  5. Any three of: a class extends one abstract class but can implement many interfaces; an abstract class can have constructors and an interface cannot; an abstract class can have ordinary per-object fields, while interface fields are all public static final constants; an abstract class can have any number of finished methods, while an interface allows bodies only on default, static and private methods; abstract classes express is-a, interfaces express can-do.
  6. The abstract class name (and any abstract method) is written in italics, or marked {abstract}. “Implements” is a dashed line from Phone to Chargeable with a hollow triangle at Chargeable, and Chargeable’s box is labelled «interface».

Test-readiness checklist#

These are the items from the course’s Exam 2 topic list that this guide covers. Tick each one only when you can do it without looking.

  • ☐  I can write an abstract class with abstract methods and explain why it cannot be instantiated.
  • ☐  I can say when to make a class abstract.
  • ☐  I can write and implement an interface, and explain when to use an interface instead of an abstract class.
  • ☐  I can draw the UML for abstract classes and methods and for interfaces, with the right arrow for each.

Test yourself in the free Kestrel Exams app

40 practice questions on abstract classes, interfaces and default methods, with every code answer checked by compiling and running it. Free, offline, no account.

Practise abstract classes and interfaces →

Frequently asked questions#

What is an abstract class in Java?

A class marked abstract that cannot be instantiated. It can contain fields, constructors and finished methods like any class, plus abstract methods: headings with no body that every concrete subclass must override. It is used for a general category whose subclasses share code but each supply some behaviour differently.

What is an abstract method?

A method declared with the abstract keyword and no body, ending in a semicolon, such as public abstract double area();. Any class containing one must be abstract, and the first non-abstract subclass must override it or the code does not compile.

What is an interface in Java?

A named set of method headings that a class promises to provide by writing implements. Interface methods are public and abstract unless they have a body (default and static methods, and since Java 9 private helper methods), and interface fields are public static final constants. A class can implement many interfaces, and an interface can be used as a variable type.

What is the difference between an abstract class and an interface?

A class can extend only one abstract class but implement many interfaces. Abstract classes can have constructors, ordinary fields and any number of finished methods; interfaces have no constructors, only constant fields, and bodies only on default, static or private methods. Abstract classes model is-a relationships among closely related classes; interfaces model abilities that unrelated classes can share.

When should I use an interface instead of an abstract class?

Use an interface when unrelated classes need to share an ability, or when a class needs more than one role, because Java allows only one superclass. Use an abstract class when closely related classes share real code or data and only a few methods differ.

How are abstract classes and interfaces shown in UML?

Abstract classes and abstract methods are written in italics or marked {abstract}. Interfaces are labelled with the stereotype <<interface>>. Inheritance from a class is a solid line with a hollow triangle at the parent; implementing an interface is a dashed line with a hollow triangle at the interface.

Suggest a change

Something here not clear? A topic you wish we covered? Tell us. We read every message, and a request is the fastest way to get a guide written — several of these exist because somebody asked.