Java Interfaces - Functional, Marker and Nested Interfaces

1. What is an Interface?

An interface in Java is a reference type that defines a contract — a set of method signatures that any implementing class must fulfil. Think of it as an agreement: "Any class that says it implements this interface promises to provide these behaviours."

Key rules for interfaces (before Java 8):

  • All methods are implicitly public abstract
  • All fields are implicitly public static final (constants)
  • Interfaces cannot be instantiated directly
  • A class can implement multiple interfaces
  • An interface can extend multiple interfaces

Java 8+ added default and static methods to interfaces. Java 9+ added private methods. The core contract concept remains unchanged.

2. Defining and Implementing an Interface

Use the interface keyword to define and implements to use it in a class. The implementing class must provide a body for every abstract method in the interface, or itself be declared abstract.

// Define the interface
public interface Drawable {
    // public abstract — implicit; no need to write them
    void draw();
    void resize(double factor);

    // Default method (Java 8+) — optional to override
    default void printInfo() {
        System.out.println("This is a Drawable object.");
    }
}

// Implement the interface in a class
public class Circle implements Drawable {
    double radius;
    String color;

    public Circle(double radius, String color) {
        this.radius = radius;
        this.color  = color;
    }

    @Override
    public void draw() {
        System.out.println("Drawing a " + color + " circle with radius " + radius);
    }

    @Override
    public void resize(double factor) {
        radius *= factor;
        System.out.println("Circle resized. New radius: " + radius);
    }
}

public class Square implements Drawable {
    double side;

    public Square(double side) {
        this.side = side;
    }

    @Override
    public void draw() {
        System.out.println("Drawing a square with side " + side);
    }

    @Override
    public void resize(double factor) {
        side *= factor;
        System.out.println("Square resized. New side: " + side);
    }

    @Override
    public void printInfo() {   // optionally override default method
        System.out.println("Square: side = " + side);
    }
}

public class Main {
    public static void main(String[] args) {
        Drawable c = new Circle(5.0, "red");  // interface reference
        Drawable s = new Square(4.0);

        c.draw();        // Drawing a red circle with radius 5.0
        c.resize(2.0);   // Circle resized. New radius: 10.0
        c.printInfo();   // This is a Drawable object.

        s.draw();        // Drawing a square with side 4.0
        s.printInfo();   // Square: side = 4.0  (overridden)
    }
}
Drawing a red circle with radius 5.0
Circle resized. New radius: 10.0
This is a Drawable object.
Drawing a square with side 4.0
Square: side = 4.0

3. Interface vs Abstract Class

Feature Interface Abstract Class
Keyword interface / implements abstract class / extends
Instantiation Cannot be instantiated Cannot be instantiated
Constructors No Yes
Instance fields No (only public static final) Yes (any access modifier)
Method types abstract, default, static (Java 8+) abstract + concrete
Multiple inheritance A class can implement many interfaces A class can extend only one
Access modifiers on methods Always public (implicit) Any access modifier
Speed Slightly slower (extra lookup) Slightly faster
Best used when Defining capabilities/contracts across unrelated classes Sharing common code & state among related classes

Rule of thumb: Prefer interfaces when you want to define a capability (e.g., Comparable, Serializable). Use abstract classes when you want to share code and state among closely related classes.

4. Multiple Interface Implementation

A Java class can implement multiple interfaces simultaneously, separated by commas. This is Java's approach to achieving multiple inheritance of type without the complexity of multiple class inheritance.

public interface Flyable {
    void fly();
    default String getMode() { return "flying"; }
}

public interface Swimmable {
    void swim();
    default String getMode() { return "swimming"; }
}

public interface Walkable {
    void walk();
}

// Duck implements all three interfaces
public class Duck implements Flyable, Swimmable, Walkable {

    private String name;

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

    @Override
    public void fly() {
        System.out.println(name + " is flying!");
    }

    @Override
    public void swim() {
        System.out.println(name + " is swimming!");
    }

    @Override
    public void walk() {
        System.out.println(name + " is walking!");
    }

    // MUST override getMode() — both Flyable and Swimmable define it
    // (this resolves the diamond problem for default methods)
    @Override
    public String getMode() {
        return Flyable.super.getMode() + " and " + Swimmable.super.getMode();
    }
}

public class Main {
    public static void main(String[] args) {
        Duck duck = new Duck("Donald");
        duck.fly();
        duck.swim();
        duck.walk();
        System.out.println("Duck modes: " + duck.getMode());

        // Polymorphic references
        Flyable   f = duck;  f.fly();
        Swimmable s = duck;  s.swim();
    }
}
Duck swims