CoreJava Java8

Java 8 – Default and Static Methods in Interfaces

Java 8 – Default and Static Methods in Interfaces

Before Java 8, interfaces could only contain abstract methods and constants. Java 8 added two new types of interface members: default methods (concrete method implementations in an interface) and static methods. This tutorial explains why they exist, how they work, and how to handle conflicts.

Why Default Methods?

Java 8 needed to add new methods (like forEach(), stream(), sort()) to existing interfaces like Collection and List without breaking the millions of classes that implement those interfaces. Default methods allow adding new methods to an interface with a default implementation, so existing implementing classes don't need to change.

Default Method Syntax

interface Vehicle {

    // Abstract method — must be implemented by all classes
    String getBrand();

    // Default method — provides a default implementation
    default String describe() {
        return "Vehicle brand: " + getBrand();
    }

    default void startEngine() {
        System.out.println(getBrand() + " engine started.");
    }
}

class Car implements Vehicle {

    private String brand;

    Car(String brand) { this.brand = brand; }

    @Override
    public String getBrand() { return brand; }

    // Does NOT need to implement describe() or startEngine() — defaults are inherited
}

class ElectricCar implements Vehicle {

    private String brand;

    ElectricCar(String brand) { this.brand = brand; }

    @Override
    public String getBrand() { return brand; }

    // Override the default if needed
    @Override
    public void startEngine() {
        System.out.println(getBrand() + " motor silently started.");
    }
}

public class DefaultMethodDemo {
    public static void main(String[] args) {
        Car car = new Car("Toyota");
        System.out.println(car.describe());   // Vehicle brand: Toyota
        car.startEngine();                     // Toyota engine started.

        ElectricCar tesla = new ElectricCar("Tesla");
        System.out.println(tesla.describe()); // Vehicle brand: Tesla
        tesla.startEngine();                  // Tesla motor silently started.
    }
}

Static Methods in Interfaces

Static interface methods are utility methods that belong to the interface, not to any instance. They can't be overridden by implementing classes.

interface MathUtils {

    static int square(int n) { return n * n; }
    static int cube(int n)   { return n * n * n; }

    static boolean isEven(int n) { return n % 2 == 0; }

    static int max(int a, int b) { return a > b ? a : b; }
}

// Call via interface name — not via an instance
System.out.println(MathUtils.square(4));    // 16
System.out.println(MathUtils.cube(3));      // 27
System.out.println(MathUtils.isEven(6));    // true
System.out.println(MathUtils.max(10, 20));  // 20

Real Example – Extending Existing Interface Behaviour

interface Printable {

    void print();   // abstract — implementors must define this

    default void printTwice() {
        print();
        print();
    }

    default void printWithBorder() {
        System.out.println("===================");
        print();
        System.out.println("===================");
    }

    static Printable of(String text) {
        return () -> System.out.println(text);
    }
}

class Report implements Printable {
    private String content;
    Report(String content) { this.content = content; }

    @Override
    public void print() {
        System.out.println("Report: " + content);
    }
}

// Usage
Report report = new Report("Q2 Sales Summary");
report.print();            // Report: Q2 Sales Summary
report.printTwice();       // Report: Q2 Sales Summary (×2)
report.printWithBorder();  // === Report: Q2 Sales Summary ===

// Factory static method
Printable hello = Printable.of("Hello from static factory!");
hello.print();   // Hello from static factory!

Default Method Conflict Resolution

When a class implements two interfaces that both define a default method with the same signature, the class must override the method to resolve the ambiguity:

interface A {
    default String greet() { return "Hello from A"; }
}

interface B {
    default String greet() { return "Hello from B"; }
}

// Compile error without override — ambiguous default method
class C implements A, B {
    @Override
    public String greet() {
        // Choose one, or call both explicitly
        return A.super.greet() + " | " + B.super.greet();
    }
}

System.out.println(new C().greet());
// Hello from A | Hello from B

Default Method vs Abstract Class

Feature Interface with Default Methods Abstract Class
Multiple inheritance Yes — a class can implement many interfaces No — a class can extend only one abstract class
State (fields) No instance fields (only constants) Yes — can have instance fields
Constructors No constructors Yes — can have constructors
Access modifiers All methods are public Can have protected/package-private methods
Use when Defining a contract + optional default behaviour for many unrelated types Sharing state and behaviour among related types

Built-in Java 8 Default Methods You Already Use

import java.util.*;
import java.util.stream.*;

List<String> names = new ArrayList<>(Arrays.asList("Charlie", "Alice", "Bob"));

// List.sort() — default method added in Java 8
names.sort(String::compareTo);
System.out.println(names);  // [Alice, Bob, Charlie]

// Iterable.forEach() — default method
names.forEach(System.out::println);

// Collection.stream() — default method (actually in Collection)
names.stream().map(String::length).forEach(System.out::println);

// Collection.removeIf() — default method
names.removeIf(n -> n.startsWith("A"));
System.out.println(names);  // [Bob, Charlie]

// Map.forEach() — default method
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
scores.forEach((name, score) -> System.out.println(name + ": " + score));

// Map.getOrDefault() — default method
int score = scores.getOrDefault("Charlie", 0);
System.out.println("Charlie: " + score);  // Charlie: 0

// Map.putIfAbsent() — default method
scores.putIfAbsent("Alice", 100);   // won't update — Alice already exists
System.out.println(scores.get("Alice"));  // 95

Summary

Default methods allow interfaces to evolve without breaking existing implementations — this is why Java 8 could add stream(), forEach(), and sort() to Collection/List without modifying every List implementation in the world. Use default methods to provide optional behaviour that implementing classes can inherit or override. Use static interface methods for utility functions related to the interface's contract. When two interfaces conflict on a default method, the implementing class must explicitly override and resolve the ambiguity using Interface.super.method() syntax.

Topics: CoreJava Java8
← Newer Post Older Post →

Comments

https://www.blogger.com/comment/frame/6690124484600543990?po=3415158923039452621&hl=en&saa=85391&origin=https://www.java9r.com