Interfaces

An interface is a contract: a list of methods a class promises to provide. Unrelated classes can implement the same interface — a Document, a Photo and an Invoice can all be Printable — and a class can implement many interfaces. Interfaces are the main tool for writing flexible, loosely coupled Java code.

Declaring and implementing

JAVA
interface Payable {
    double amountDue();
}

class Invoice implements Payable {
    private final double total;
    Invoice(double total) { this.total = total; }
    @Override public double amountDue() { return total; }
}

class Subscription implements Payable {
    private final double monthly;
    private final int months;
    Subscription(double monthly, int months) { this.monthly = monthly; this.months = months; }
    @Override public double amountDue() { return monthly * months; }
}

public class Main {
    public static void main(String[] args) {
        Payable[] bills = {new Invoice(120.0), new Subscription(9.99, 3)};
        double total = 0;
        for (Payable p : bills) total += p.amountDue();
        System.out.println("Total due: %.2f".formatted(total));
    }
}
Output
Total due: 149.97

Interface methods are implicitly public and abstract, so implementations must be public. Code that works with Payable doesn't care what kind of bill it is.

Implementing several interfaces

JAVA
interface Named {
    String name();
}

interface Priced {
    double price();
}

class Product implements Named, Priced, Comparable<Product> {
    private final String name;
    private final double price;
    Product(String name, double price) { this.name = name; this.price = price; }
    @Override public String name() { return name; }
    @Override public double price() { return price; }
    @Override public int compareTo(Product other) { return Double.compare(price, other.price); }
}

public class Main {
    public static void main(String[] args) {
        Product a = new Product("Mug", 12.5);
        Product b = new Product("Lamp", 34.0);
        Product cheaper = a.compareTo(b) < 0 ? a : b;
        System.out.println(cheaper.name());
    }
}
Output
Mug

A class extends at most one class but can implement any number of interfaces. Standard interfaces like Comparable (natural ordering, lesson 43), Iterable (usable in for-each) and AutoCloseable (try-with-resources, lesson 37) plug your classes into Java's built-in features.

Program to the interface

JAVA
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Main {
    static int countLong(List<String> words) {
        int n = 0;
        for (String w : words) if (w.length() > 4) n++;
        return n;
    }

    public static void main(String[] args) {
        List<String> a = new ArrayList<>(List.of("apple", "fig", "banana"));
        List<String> b = new LinkedList<>(List.of("kiwi", "cherry"));
        System.out.println(countLong(a) + " " + countLong(b));
    }
}
Output
2 1

Declaring variables and parameters with interface types (List) rather than concrete classes (ArrayList) means you can switch implementations without changing the code that uses them.

Default and static methods

JAVA
interface Greeter {
    String name();

    default String greet() {
        return "Hello, " + name() + "!";
    }

    static Greeter of(String name) {
        return () -> name;
    }
}

class Robot implements Greeter {
    @Override public String name() { return "R2"; }
    @Override public String greet() { return "BEEP. " + name() + " ONLINE."; }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Greeter.of("Ana").greet());
        System.out.println(new Robot().greet());
    }
}
Output
Hello, Ana!
BEEP. R2 ONLINE.

Since Java 8, interfaces can include default methods (with a body, which implementations may override) and static helper methods. Default methods let interfaces grow without breaking existing classes. () -> name is a lambda — an interface with a single abstract method can be implemented that briefly (lesson 45).

Constants in interfaces

JAVA
interface Limits {
    int MAX_ITEMS = 50;
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Limits.MAX_ITEMS);
    }
}
Output
50

Interface fields are automatically public static final. It's usually better to put constants in the class they belong to.

Interfaces extending interfaces

JAVA
interface Readable { String read(); }
interface Writable { void write(String text); }
interface ReadWrite extends Readable, Writable {}

class Notepad implements ReadWrite {
    private final StringBuilder content = new StringBuilder();
    @Override public String read() { return content.toString(); }
    @Override public void write(String text) { content.append(text); }
}

public class Main {
    public static void main(String[] args) {
        ReadWrite pad = new Notepad();
        pad.write("Buy milk. ");
        pad.write("Call Ana.");
        System.out.println(pad.read());
    }
}
Output
Buy milk. Call Ana.

Try it yourself

Define a Discount interface with several implementations and apply them to a price.

Try it yourself · Main.java
Edit the code, copy it into Main.java and run java Main.java (Java 25+). How to set up Java
Output
NoDiscount -> 49.90
15.0% off  -> 42.42
FixedOff   -> 39.90