Skip to content

Java Interfaces and Functional Interfaces

Question

Explain the concept of interfaces in Java, including functional interfaces, default methods, and static methods. What are their uses, benefits, and best practices?

Answer

Interfaces in Java provide a way to achieve abstraction and define a contract that classes must implement. Functional interfaces are a special type of interface that contains exactly one abstract method, making them suitable for lambda expressions.

Basic Interface

public interface Drawable {
    void draw();  // Abstract method

    // Default method (Java 8+)
    default void resize() {
        System.out.println("Resizing...");
    }

    // Static method (Java 8+)
    static void clear() {
        System.out.println("Clearing...");
    }
}

public class Circle implements Drawable {
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

Functional Interfaces

  1. Basic Functional Interface

    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
    
    // Usage with lambda
    Predicate<String> isEmpty = str -> str.isEmpty();
    

  2. Common Functional Interfaces

    // Consumer - takes input, returns nothing
    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }
    
    // Function - takes input, returns output
    @FunctionalInterface
    public interface Function<T, R> {
        R apply(T t);
    }
    
    // Supplier - takes nothing, returns output
    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
    
    // Runnable - takes nothing, returns nothing
    @FunctionalInterface
    public interface Runnable {
        void run();
    }
    

Default Methods

  1. Basic Default Method

    public interface Vehicle {
        void start();
    
        default void stop() {
            System.out.println("Vehicle stopped");
        }
    }
    
    public class Car implements Vehicle {
        @Override
        public void start() {
            System.out.println("Car started");
        }
        // stop() method is inherited
    }
    

  2. Multiple Inheritance Resolution

    public interface A {
        default void method() {
            System.out.println("A");
        }
    }
    
    public interface B {
        default void method() {
            System.out.println("B");
        }
    }
    
    public class C implements A, B {
        @Override
        public void method() {
            A.super.method();  // Explicitly call A's method
            B.super.method();  // Explicitly call B's method
        }
    }
    

Static Methods

  1. Interface Static Methods

    public interface MathOperations {
        static int add(int a, int b) {
            return a + b;
        }
    
        static int subtract(int a, int b) {
            return a - b;
        }
    }
    
    // Usage
    int sum = MathOperations.add(5, 3);
    

  2. Utility Methods

    public interface StringUtils {
        static boolean isEmpty(String str) {
            return str == null || str.trim().isEmpty();
        }
    
        static String reverse(String str) {
            return new StringBuilder(str).reverse().toString();
        }
    }
    

Private Methods (Java 9+)

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

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

    private void log(String level, String message) {
        System.out.println(level + ": " + message);
    }
}

Interface Inheritance

  1. Multiple Interface Implementation

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

  2. Interface Extension

    public interface Animal {
        void eat();
    }
    
    public interface Pet extends Animal {
        void play();
    }
    
    public class Dog implements Pet {
        @Override
        public void eat() {
            System.out.println("Dog is eating");
        }
    
        @Override
        public void play() {
            System.out.println("Dog is playing");
        }
    }
    

Best Practices

  1. Interface Segregation

    // Bad - Too many methods
    public interface Worker {
        void work();
        void eat();
        void sleep();
        void play();
    }
    
    // Better - Segregated interfaces
    public interface Workable {
        void work();
    }
    
    public interface Eatable {
        void eat();
    }
    
    public interface Sleepable {
        void sleep();
    }
    
    public class Human implements Workable, Eatable, Sleepable {
        // Implement only needed methods
    }
    

  2. Default Method Usage

    public interface Collection<E> {
        int size();
        boolean isEmpty();
    
        default boolean isNotEmpty() {
            return !isEmpty();
        }
    
        default boolean hasSize(int size) {
            return size() == size;
        }
    }
    

Common Use Cases

  1. Callback Pattern

    public interface Callback {
        void onSuccess(String result);
        void onError(Exception e);
    }
    
    public class AsyncTask {
        public void execute(Callback callback) {
            try {
                // Do something
                callback.onSuccess("Result");
            } catch (Exception e) {
                callback.onError(e);
            }
        }
    }
    

  2. Strategy Pattern

    public interface PaymentStrategy {
        void pay(int amount);
    }
    
    public class ShoppingCart {
        private PaymentStrategy paymentStrategy;
    
        public void setPaymentStrategy(PaymentStrategy strategy) {
            this.paymentStrategy = strategy;
        }
    
        public void checkout(int amount) {
            paymentStrategy.pay(amount);
        }
    }
    

Modern Java Features

  1. Sealed Interfaces (Java 17+)

    public sealed interface Shape permits Circle, Rectangle {
        double getArea();
    }
    
    public record Circle(double radius) implements Shape {
        @Override
        public double getArea() {
            return Math.PI * radius * radius;
        }
    }
    

  2. Record Components in Interfaces

    public interface Point {
        record Component(int x, int y) {}
    
        Component getPosition();
    }
    

Common Pitfalls

  1. Default Method Conflicts

    public interface A {
        default void method() {
            System.out.println("A");
        }
    }
    
    public interface B {
        default void method() {
            System.out.println("B");
        }
    }
    
    public class C implements A, B {
        // Must override method to resolve conflict
        @Override
        public void method() {
            A.super.method();
        }
    }
    

  2. Interface Pollution

    // Bad - Too many unrelated methods
    public interface Utility {
        void processData();
        void formatText();
        void calculateTax();
        void sendEmail();
    }
    
    // Better - Separate interfaces
    public interface DataProcessor {
        void processData();
    }
    
    public interface TextFormatter {
        void formatText();
    }
    
    public interface TaxCalculator {
        void calculateTax();
    }
    
    public interface EmailSender {
        void sendEmail();
    }