Interfaces

An interface is a pure contract — a promise of behavior any unrelated class can agree to implement, letting Java simulate the flexibility of multiple inheritance safely.

An interface declares a set of methods that implementing classes agree to provide, without (traditionally) providing any implementation itself. It defines what a class can do, not how — any class, regardless of where it sits in the class hierarchy, can implement an interface and be treated as that interface's type. Since Java 8, interfaces can also include default and static methods with actual bodies, and since Java 9, private methods for internal reuse.

Interface Declaration

An interface is declared with the interface keyword instead of class. Its methods are implicitly public and abstract unless marked default, static, or private.

Movable declares only a signature — no class need be involved at all yet; it is purely a contract.

public interface Movable {
    void move(int dx, int dy); // implicitly public abstract
}

Implementation

A class agrees to an interface's contract with the implements keyword, and must provide public implementations for all of the interface's abstract methods (unless the class itself is abstract).

Robot fulfills Movable's contract, so a Robot reference can be used anywhere a Movable is expected — this is polymorphism through an interface type.

public class Robot implements Movable {
    private int x, y;

    @Override
    public void move(int dx, int dy) { // must be public: interface methods are implicitly public
        x += dx;
        y += dy;
    }
}

Movable m = new Robot(); // a Robot IS-A Movable
m.move(3, 4);

Abstract Methods in Interfaces

Ordinary interface methods (no body, just a signature ending in a semicolon) are implicitly public and abstract — writing those modifiers explicitly is allowed but redundant. Every implementing class must supply a matching public method.

No modifier is required; the compiler treats an interface method with no body as public abstract by default.

public interface Comparable2<T> {
    int compareTo(T other); // implicitly "public abstract"
}

Default Methods

A default method (Java 8+) provides an actual implementation inside the interface itself, marked with the default keyword. Implementing classes inherit this implementation automatically but may override it if they need different behavior. Default methods let an interface evolve — adding new methods — without breaking every class that already implements it.

Person never wrote greet() itself — it got a working implementation for free from the interface's default method, built on top of the abstract name() method Person does implement.

public interface Greeter {
    String name();

    default void greet() { // has a real body
        System.out.println("Hello, " + name() + "!");
    }
}

public class Person implements Greeter {
    private final String personName;
    Person(String personName) { this.personName = personName; }

    @Override
    public String name() { return personName; }
    // greet() is inherited from Greeter with no extra code needed
}

new Person("Ada").greet(); // "Hello, Ada!"

Static Interface Methods

An interface can declare static methods (Java 8+), which belong to the interface itself rather than to any implementing instance. They are called as InterfaceName.method(...) and are commonly used for factory methods or utility logic related to the interface.

nonEmpty() is called directly on the interface type, not on any implementing object — it exists to produce Validator instances, similar to a static factory method on a class.

public interface Validator {
    boolean isValid(String input);

    static Validator nonEmpty() { // static factory method
        return input -> input != null && !input.isBlank();
    }
}

Validator v = Validator.nonEmpty();
System.out.println(v.isValid(""));    // false
System.out.println(v.isValid("hi"));  // true

Private Interface Methods

Since Java 9, an interface can declare private methods (with a body) to share logic between its own default and static methods, without exposing that helper method as part of the public contract implementing classes see.

log(...) exists purely to avoid duplicating formatting logic between logInfo and logError; implementing classes never see it and cannot call it directly.

public interface Logger {
    default void logInfo(String msg) {
        log("INFO", msg);
    }

    default void logError(String msg) {
        log("ERROR", msg);
    }

    private void log(String level, String msg) { // internal helper, not part of the public API
        System.out.println("[" + level + "] " + msg);
    }
}

Constants in Interfaces

Any field declared in an interface is implicitly public, static, and final — effectively a constant, shared by the interface type itself, not per-instance state. This is a key contrast with abstract classes, which can hold genuine per-instance fields.

SPEED_OF_LIGHT behaves like a static final field on a class — there is exactly one shared value, accessed through the interface name, never per-object.

Because interface fields are always static and final, an interface cannot declare mutable, per-instance state — that is one of the main practical reasons to reach for an abstract class instead when subclasses need shared, changeable fields.

public interface PhysicsConstants {
    double SPEED_OF_LIGHT = 299_792_458.0; // implicitly public static final
}

System.out.println(PhysicsConstants.SPEED_OF_LIGHT);

Implementing Multiple Interfaces

Unlike class inheritance, a single class can implement any number of interfaces, separated by commas. This is Java's primary mechanism for giving one class multiple, independent contracts.

Duck satisfies two entirely independent contracts at once — something a single superclass could never provide, since Java allows only one extends.

If two implemented interfaces both declare a default method with the same signature, the implementing class must explicitly override it to resolve the conflict — the compiler will not pick one arbitrarily.

interface Flyable { void fly(); }
interface Swimmable { void swim(); }

class Duck implements Flyable, Swimmable {
    @Override public void fly() { System.out.println("flying"); }
    @Override public void swim() { System.out.println("swimming"); }
}

Duck d = new Duck();
Flyable f = d;    // usable as a Flyable
Swimmable s = d;  // and independently as a Swimmable

Interface as Contract

The core idea behind an interface is separating what a type can do from how it does it, and from what it is (its class hierarchy). Code written against an interface type can work with any class that implements it — including classes written long after that code, and classes with completely unrelated superclasses — as long as they honor the contract.

checkout() knows nothing about CreditCard or GiftCard specifically — it only relies on the PaymentMethod contract, so a brand-new payment type can be added later without touching checkout() at all.

interface PaymentMethod {
    void pay(double amount);
}

class CreditCard implements PaymentMethod {
    @Override public void pay(double amount) { System.out.println("Charged $" + amount + " to card"); }
}

class GiftCard implements PaymentMethod {
    @Override public void pay(double amount) { System.out.println("Deducted $" + amount + " from gift card"); }
}

void checkout(PaymentMethod method, double total) {
    method.pay(total); // works for ANY current or future PaymentMethod implementation
}

Practical Usage: Interface versus Abstract Class

Reach for an interface when unrelated classes need to share only a behavioral contract (possibly several, since a class can implement many interfaces), and no shared mutable state is needed. Reach for an abstract class (see the Abstract Classes topic) when a family of closely related classes needs to share real instance state and inherited implementation, since a class can extend only one class.

Aspect                      Interface                        Abstract class
--------------------------  --------------------------------  ---------------------------------
Instance (mutable) fields    no -- fields are static final     yes -- ordinary instance fields
Constructors                 no                                 yes
Multiple per class            yes -- implement many              no -- extend only one
Method bodies                 default/static/private only        any method can have a body
Typical use                   a shared capability/contract        a shared "is-a" family with state

Complete Example

Shape combines an abstract method (area), a default method (printArea) built on top of it, and a static factory method (unitSquare) — all three interface features working together.

interface Shape {
    double area();

    default void printArea() { // default method built on the abstract one
        System.out.println("Area: " + area());
    }

    static Shape unitSquare() { // static factory method
        return () -> 1.0; // lambda implementing the single abstract method
    }
}

class Circle implements Shape {
    private final double radius;
    Circle(double radius) { this.radius = radius; }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape c = new Circle(2);
        c.printArea();                 // uses the inherited default method

        Shape unit = Shape.unitSquare(); // uses the static factory method
        unit.printArea();
    }
}

Mental Model

Think of an interface as a job description rather than an employee: it lists what tasks must be performed (abstract methods), and may include shared standard procedures everyone can use as-is (default methods) or company-wide reference tools (static methods) — but it hires no one and has no personal belongings (no instance state) of its own. Any class, from any department (any class hierarchy), can take the job as long as it can perform every required task.

Quick Reference

Interface member type       Modifiers (implicit unless stated)     Has a body?
---------------------------  --------------------------------------  ------------
plain method                  public abstract                          no
default method                 public                                   yes
static method                  public                                   yes
private method (Java 9+)       private                                  yes (required)
field                           public static final                     N/A (constant)

Key Takeaways

An interface declares a contract of methods that any implementing class, from any part of the class hierarchy, agrees to fulfill.

Plain interface methods are implicitly public and abstract; fields are implicitly public, static, and final.

default methods (with bodies) let interfaces provide shared, overridable behavior and evolve without breaking existing implementers.

static methods belong to the interface type itself; private methods (Java 9+) share logic between default/static methods internally.

A class can implement multiple interfaces, unlike single class inheritance, making interfaces Java's way of combining independent contracts.

interface Movable { void move(int dx, int dy); } class Robot implements Movable { void move(int dx, int dy) { } // compile error: reduces implicit "public" visibility } interface Movable { void move(int dx, int dy); } class Robot implements Movable { @Override public void move(int dx, int dy) { } // must be public -- matches the implicit interface modifier }

Interface methods are implicitly public; an implementing class's override cannot use a narrower access modifier, exactly like overriding rules for classes.

interface Config { int MAX_RETRIES = 3; } class Retryer implements Config { void run() { MAX_RETRIES = 5; // compile error: interface fields are implicitly final } } interface Config { int MAX_RETRIES = 3; // implicitly public static final -- a true constant } class Retryer implements Config { void run() { int attempts = MAX_RETRIES; // read it, don't try to reassign it } }

Every interface field is implicitly public static final, so it can never be reassigned — treating it as mutable per-instance state is a misunderstanding of how interface fields work.

interface Flyable { default void move() { System.out.println("flying"); } } interface Swimmable { default void move() { System.out.println("swimming"); } } class Duck implements Flyable, Swimmable { // compile error: two default methods with the same signature conflict -- must be resolved explicitly } interface Flyable { default void move() { System.out.println("flying"); } } interface Swimmable { default void move() { System.out.println("swimming"); } } class Duck implements Flyable, Swimmable { @Override public void move() { Flyable.super.move(); // explicitly choose (or combine) one } }

When two implemented interfaces provide conflicting default methods, Java refuses to guess which one to use — the implementing class must override the method and resolve the conflict explicitly.

interface Shape { double area(); } Shape s = new Shape(); // compile error: interfaces cannot be instantiated directly interface Shape { double area(); } Shape s = () -> 9.0; // OK: a lambda supplying the single abstract method // or: Shape s = new Circle(3); with a concrete implementing class

An interface, like an abstract class, can never be instantiated directly with new — it needs a concrete implementing class (or, for a single-abstract-method interface, a lambda expression) to provide an actual object.

What are the implicit modifiers on a plain (no-body) interface method, and on an interface field?

A plain interface method is implicitly public and abstract. An interface field is implicitly public, static, and final — effectively a shared constant.

What is a default method, and why was it added to the language?

A default method is an interface method with an actual body, marked default, which implementing classes inherit automatically but can override. It was added so interfaces could gain new methods over time without breaking every class that already implements them (since existing classes automatically get the default implementation instead of failing to compile).

Can a single class implement more than one interface? Can it extend more than one class?

A class can implement any number of interfaces at once. It can extend only one class, since Java supports single class inheritance — interfaces are the language's mechanism for combining multiple independent contracts.

What happens if a class implements two interfaces that each declare a default method with the same signature?

The compiler requires the implementing class to explicitly override the method and resolve the conflict itself (optionally calling InterfaceName.super.method() to reuse one of the original implementations) — it will not silently pick one.

Why can't an interface hold ordinary, mutable instance fields the way an abstract class can?

Every field declared in an interface is implicitly public, static, and final — there is exactly one shared, unchangeable value per field on the interface itself, not per-instance state, so mutable per-object fields are not possible in an interface.

What is the purpose of a private method inside an interface (Java 9+)?

It lets default and static methods within the same interface share common logic through an internal helper method, without exposing that helper as part of the interface's public contract to implementing classes.

Interview-style: why is "programming to an interface" considered good design?

Code written against an interface type depends only on the contract, not on any specific implementing class or its position in a class hierarchy — new implementations can be introduced later, including ones written by other teams, and the existing code keeps working without modification, as long as the contract is honored.

Write a Playable interface with an abstract method play(), a default method playTwice() that calls play() twice, and a Song class implementing it.

interface Playable { void play(); default void playTwice() { play(); play(); } } class Song implements Playable { private final String title; Song(String title) { this.title = title; } @Override public void play() { System.out.println("Playing: " + title); } } playTwice() is inherited automatically by Song from the default method; it relies entirely on whatever play() implementation the implementing class supplies, without Song needing to write playTwice() itself.

What does this print, and why is the override required?

Prints "AB". Because both A and B declare a default method id() with the same signature, C is required to override id() itself to resolve the conflict; the override explicitly calls both original implementations via A.super.id() and B.super.id() and concatenates them.

Write a Named interface with an abstract method name(), a static factory method of(String) returning a Named via a lambda, and demonstrate calling it.

interface Named { String name(); static Named of(String value) { return () -> value; // lambda implementing the single abstract method } } public class Main { public static void main(String[] args) { Named n = Named.of("Ada"); System.out.println(n.name()); } }

Named.of(...) is a static interface method acting as a factory; it returns a lambda that implements the single abstract method name(), producing a working Named instance without any named implementing class.

What is printed, and could Chest change MAX_ITEMS at runtime?

Prints 10. Chest cannot change MAX_ITEMS at runtime, because every interface field is implicitly public static final — it behaves as a single shared constant on the Vault interface itself, not as per-instance mutable state, so any reassignment attempt would be a compile error.

interface A { default String id() { return "A"; } }
interface B { default String id() { return "B"; } }
class C implements A, B {
    @Override
    public String id() {
        return A.super.id() + B.super.id();
    }
}
System.out.println(new C().id());
interface Vault {
    int MAX_ITEMS = 10; // implicitly public static final
}
class Chest implements Vault {
    void fill() {
        System.out.println(MAX_ITEMS);
    }
}
new Chest().fill();