Skip to content

Interfaces

Interfaces#

An interface is a contract that defines method signatures that implementing classes must provide. It specifies what operations a class must support without defining how they should be implemented, enabling polymorphism and loose coupling.

Think of interfaces as job descriptions—they define required skills without specifying implementation details. For example, a "Playable" interface might require a play() method, but doesn't care whether it's implemented by a guitar, piano, or violin.

Interface Definition#

An interface defines the contract that classes must follow:

graph LR
    A["Interface"] --> B["Method Signatures"]
    B --> C["play()"]
    B --> D["stop()"]
    B --> E["getVolume()"]

    style A fill:#e3f2fd
    style B fill:#c8e6c9

Implementation by Classes#

Different classes can implement the same interface with their own specific behavior:

graph TD
    A["Playable Interface"] --> B["Guitar Class"]
    A --> C["Piano Class"]
    A --> D["Violin Class"]

    B --> E["Guitar.play()<br/>Strumming strings"]
    C --> F["Piano.play()<br/>Pressing keys"]
    D --> G["Violin.play()<br/>Bowing strings"]

    style A fill:#e3f2fd
    style E fill:#fff3e0
    style F fill:#fff3e0
    style G fill:#fff3e0

Polymorphic Usage#

Client code can work with any class that implements the interface:

graph LR
    A["Client Code"] --> B["Uses Playable interface"]
    B --> C["Polymorphic behavior"]
    C --> D["Same method call<br/>Different implementations"]

    style A fill:#e8f5e8
    style C fill:#c8e6c9

This creates loose coupling—you can add new instruments or modify existing ones without changing client code, as long as they implement the same interface contract.

Language Examples#

public interface Playable {
    void play();
}

public class Guitar implements Playable {
    @Override
    public void play() {
        System.out.println("Strumming the guitar!");
    }
}
interface Playable {
    fun play()
}

class Guitar : Playable {
    override fun play() {
        println("Strumming the guitar!")
    }
}
interface Playable {
    play(): void;
}

class Guitar implements Playable {
    play(): void {
        console.log("Strumming the guitar!");
    }
}
abstract class Playable {
    void play();
}

class Guitar implements Playable {
    @override
    void play() {
        print("Strumming the guitar!");
    }
}
protocol Playable {
    func play()
}

class Guitar: Playable {
    func play() {
        print("Strumming the guitar!")
    }
}
from abc import ABC, abstractmethod

class Playable(ABC):
    @abstractmethod
    def play(self):
        pass

class Guitar(Playable):
    def play(self):
        print("Strumming the guitar!")