Skip to content

Interface

An interface in Java is a fully abstract "contract" that a class can agree to follow. It defines a set of method signatures as a blueprint for behavior, it doesn't provide the implementation for those methods. The class that implements the interface must must implement these methods.

An interface defines what a class can do, without specifying how it does it. This allows different classes to implement the same interface in their own unique ways, enabling to write flexible code that works with any object that adheres to the contract.

Its primary significance is to achieve abstraction and polymorphism.

Defining and Implementing an Interface

Interface is defined using the interface keyword. A class then uses the implements keyword to adopt the contract and must provide a concrete implementation for all of the interface's methods.

Defining an Interface:

java
interface Drivable {
    void startEngine();
    void stopEngine();
    void turn(String direction);
}

Implementing the Interface:

java
class Car implements Drivable {
    @Override
    public void startEngine() {
        System.out.println("Car engine started.");
    }
    
    @Override
    public void stopEngine() {
        System.out.println("Car engine stopped.");
    }
    
    @Override
    public void turn(String direction) {
        System.out.println("Car is turning " 
	        + direction);
    }
}

Implementing Multiple Interfaces

Java allows a class to implement multiple interfaces. This is how Java supports a form of multiple inheritance (multiple inheritance of type, not state).

A single class can agree to multiple contracts, combining behaviors from different sources. List the interfaces after the implements keyword, separated by commas.

A Smartphone can act as both a Phone and a Camera.

java
interface Phone {
    void makeCall(String number);
}

interface Camera {
    void takePicture();
}

// This class implements both interfaces
class Smartphone implements Phone, Camera {
    @Override
    public void makeCall(String number) {
        System.out.println("Calling " + number + "...");
    }

    @Override
    public void takePicture() {
        System.out.println("Click! Picture taken.");
    }
}

public class MultiInterfaceDemo {
    public static void main(String[] args) {
        Smartphone myPhone = new Smartphone();
        myPhone.makeCall("123-456-7890");
        myPhone.takePicture();
    }
}

Classes vs. Interfaces

The key difference is that a class describes both attributes and behaviors, while an interface only describes behaviors.

FeatureClassInterface
PurposeTo be a blueprint for objects.To be a contract for what a class can do.
InstantiationYou can create an instance (object) of a class.You cannot create an instance of an interface.
MethodsCan have both abstract and concrete methods.Traditionally, only abstract methods. (Java 8+ allows default and static methods).
VariablesCan have instance variables of any kind.Can only have public static final constants.
ConstructorsCan have constructors.Cannot have constructors.
InheritanceA class can extend only one superclass.A class can implement multiple interfaces.
Keywordclassinterface

extends vs. implements

extends and implements are both used for inheritance, but they define different kinds of relationships.

  • extends: Creates a parent-child relationship between two classes. The subclass inherits the state (fields) and behavior (methods) from its one and only superclass. It represents an "is-a" relationship (e.g., a Dog is an Animal).

  • implements: Creates a contract relationship between a class and one or more interfaces. The class agrees to provide the implementation for the methods defined in the interface. It represents a "can-do" relationship (e.g., a Bird can Fly).

A Parrot is an Animal (inheritance) and can Fly (behavior).

java
// Superclass
class Animal {
    void eat() {
        System.out.println("This animal eats.");
    }
}

// Interface
interface Flyable {
    void fly();
}

// Class that extends one class and implements one interface
class Parrot extends Animal implements Flyable {
    @Override
    public void fly() {
        System.out.println("The parrot is flying.");
    }
}

public class ExtendsVsImplementsDemo {
    public static void main(String[] args) {
        Parrot polly = new Parrot();
        polly.eat(); // Inherited from Animal
        polly.fly(); // Implemented from Flyable
    }
}

Default Interface Methods

A default method is a method in an interface that has a body. It was introduced in Java 8 to allow new methods to be added to existing interfaces without breaking the classes that already implement them.

If a class implements an interface with a default method, it can either use the default implementation directly or override it with its own specific logic.

java
interface Alarm {
    void setAlarm();

    // A default method with a pre-defined implementation
    default void snooze() {
        System.out.println("Snoozing for 5 minutes.");
    }
}

class MyClock implements Alarm {
    @Override
    public void setAlarm() {
        System.out.println("Alarm is set!");
    }
    
    // This class does not override snooze(), 
    // so it will use the default one.
}

Interface Programs

1. Stack Implementation Using an Interface

Defining Stack interface and then provides a concrete implementation using an ArrayList or LinkedList. The main class then demonstrates the Last-In, First-Out (LIFO) behavior of the stack.

java
import java.util.LinkedList;

interface IStack {
    void push(int item); // Add an item to the top
    int pop();     // Remove and return the top item
    int peek();   // Return the top item without removing
    boolean isEmpty();
}

// class that implements the Stack interface using a LinkedList
class LinkedStack implements IStack {
    private LinkedList<Integer> stackList;

    public LinkedStack() {
        stackList = new LinkedList<>();
    }
	
    @Override
    public boolean isEmpty() {
        return stackList.isEmpty();
    }
	
	@Override
    public void push(int item) {
        stackList.addFirst(item);
        System.out.println("Pushed: " + item);
    }
	
    @Override
    public int pop() {
        if (!isEmpty()) {
            int item = stackList.removeFirst();
            System.out.println("Popped: " + item);
            return item;
        } else {
            System.out.println("Stack is empty. Cannot pop.");
            return -1; // Or throw an exception
        }
    }
	
    @Override
    public int peek() {
        if (!isEmpty()) {
            return stackList.getFirst();
        }
        return -1; // Or throw an exception
    }
}

public class StackDemo {
    public static void main(String[] args) {
    
        IStack myStack = new LinkedStack();

        myStack.push(10);
        myStack.push(20);
        myStack.push(30);

        System.out.println("Top element is: " 
	        + myStack.peek());

        myStack.pop();
        myStack.pop();
        myStack.pop();
        myStack.pop(); // Attempt to pop from an empty stack
    }
}

2. Stack interface using an ArrayList

java
import java.util.ArrayList;

interface IStack {
    void push(int item); // Add an item to the top
    int pop();      // Remove and return the top item
    int peek();   // Return the top item without removing
    boolean isEmpty();
}

class ArrayStack implements IStack {
    private ArrayList<Integer> stackList;

    public ArrayStack() {
        stackList = new ArrayList<>();
    }

    @Override
    public void push(int item) {
        stackList.add(item);
        System.out.println("Pushed: " + item);
    }

    @Override
    public int pop() {
        if (!isEmpty()) {
            int lastIndex = stackList.size() - 1;
            int item = stackList.remove(lastIndex);
            System.out.println("Popped: " + item);
            return item;
        } else {
            System.out.println("Stack is empty. Cannot pop.");
            return -1; // Or throw an exception
        }
    }

    @Override
    public int peek() {
        if (!isEmpty()) {
            int lastIndex = stackList.size() - 1;
            return stackList.get(lastIndex);
        }
        return -1; // Or throw an exception
    }

    @Override
    public boolean isEmpty() {
        return stackList.isEmpty();
    }
}

public class StackDemo {
    public static void main(String[] args) {
        IStack myStack = new ArrayStack();

        myStack.push(10);
        myStack.push(20);
        myStack.push(30);

        System.out.println("Top element is: " + myStack.peek());

        myStack.pop();
        myStack.pop();
        myStack.pop();
        myStack.pop(); // Attempt to pop from an empty stack
    }
}

3. Queue Implementation Using an Interface

Defining a Queue interface and implements it using a LinkedList. The demo shows the First-In, First-Out (FIFO) behavior.

java
import java.util.LinkedList;

interface IQueue {
    void enqueue(String item); // Add an item to the end
    String dequeue();   // Remove, return item from the front
    String peek();     // Return front item without removing
    boolean isEmpty();
}

class LinkedQueue implements IQueue {
    private LinkedList<String> queueList;

    public LinkedQueue() {
        queueList = new LinkedList<>();
    }

    @Override
    public void enqueue(String item) {
        queueList.addLast(item);
        System.out.println("Enqueued: " + item);
    }

    @Override
    public String dequeue() {
        if (!isEmpty()) {
            String item = queueList.removeFirst();
            System.out.println("Dequeued: " + item);
            return item;
        } else {
            System.out.println("Queue is empty. Cannot dequeue.");
            return null; // Or throw an exception
        }
    }

    @Override
    public String peek() {
        if (!isEmpty()) {
            return queueList.getFirst();
        }
        return null;
    }

    @Override
    public boolean isEmpty() {
        return queueList.isEmpty();
    }
}

public class QueueDemo {
    public static void main(String[] args) {
        IQueue myQueue = new LinkedQueue();

        myQueue.enqueue("Alice");
        myQueue.enqueue("Bob");
        myQueue.enqueue("Charlie");

        System.out.println("Front of queue is: " 
	        + myQueue.peek());

        myQueue.dequeue();
        myQueue.dequeue();
        myQueue.dequeue();
        myQueue.dequeue(); // Attempt to dequeue from an empty queue
    }
}

4. Searchable Interface with Multiple Implementations

The Searchable interface defines a contract for searching, which is then implemented using two different algorithms: linear search and binary search.

java
import java.util.Arrays;

interface Searchable {
    int search(int[] arr, int key);
}

class LinearSearch implements Searchable {
    @Override
    public int search(int[] arr, int key) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == key) {
                return i; // Found it
            }
        }
        return -1; // Not found
    }
}

class BinarySearch implements Searchable {
    @Override
    public int search(int[] arr, int key) {
        // Note: Binary search requires the array to be sorted.
        int low = 0;
        int high = arr.length - 1;

        while (low <= high) {
            int mid = (high + low) / 2;
            if (arr[mid] < key) {
                low = mid + 1;
            } else if (arr[mid] > key) {
                high = mid - 1;
            } else {
                return mid; // Found it
            }
        }
        return -1; // Not found
    }
}

public class SearchDemo {
    public static void main(String[] args) {
        int[] numbers = { 5, 2, 8, 1, 9, 4 };
        int key = 9;

        // Use Linear Search
        Searchable linear = new LinearSearch();
        int index1 = linear.search(numbers, key);
        System.out.println("Linear Search: Key " 
	        + key + " found at index " + index1);

        // For Binary Search, the array must be sorted first.
        Arrays.sort(numbers);
        System.out.println("Sorted array for Binary Search: " 
	        + Arrays.toString(numbers));
        
        // Use Binary Search
        Searchable binary = new BinarySearch();
        int index2 = binary.search(numbers, key);
        System.out.println("Binary Search: Key " 
	        + key + " found at index " + index2);
    }
}

5. Shape Interface with Multiple Implementations

Defining a Shape interface with a calculateArea() method. Three different classes—Circle, Square, and Triangle—implement this interface, each providing the correct formula for its area.

java
import java.util.ArrayList;
import java.util.List;

interface Shape {
    // public, static, and final by default
    double PI = 3.14159;
    
    double calculateArea();  // An abstract method
}

class Circle implements Shape {
    private double radius;

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

    @Override
    public double calculateArea() {
        return PI * radius * radius;
    }
}

class Square implements Shape {
    private double side;

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

    @Override
    public double calculateArea() {
        return side * side;
    }
}

class Triangle implements Shape {
    private double base;
    private double height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    @Override
    public double calculateArea() {
        return 0.5 * base * height;
    }
}

public class ShapeInterfaceDemo {
    public static void main(String[] args) {
        // Create a list of Shape objects
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(5.0));
        shapes.add(new Square(4.0));
        shapes.add(new Triangle(6.0, 3.0));

        // demonstrates polymorphism
        for (Shape shape : shapes) {
            System.out.printf("Area of %s is %.2f\n", 
                shape.getClass().getSimpleName(), 
                shape.calculateArea());
        }
    }
}

main function without using List and iteration to print

java
public class ShapeInterfaceDemo {
    public static void main(String[] args) {
        
        Shape myCircle = new Circle(5.0); 
        
        Shape mySquare = new Square(4.0);
        
        Shape myTriangle = new Triangle(6.0, 3.0);

        System.out.printf("Area of the Circle is %.2f\n",
	         myCircle.calculateArea());

        System.out.printf("Area of the Square is %.2f\n",
	         mySquare.calculateArea());

        System.out.printf("Area of the Triangle is %.2f\n",
	         myTriangle.calculateArea());
    }
}

Made with ❤️ for students, by a fellow learner.