You want to add a new method to an interface used by 100 classes. Without default methods, all 100 must be updated. Default methods let you add new methods with implementations - existing code keeps working.

Basic default method

Provide implementation in an interface.

example
BasicDefault.java
Replay: real traced execution (multi-file project)
// Basic Default Method Syntax

interface Greeting {
    // Abstract method - MUST be implemented
    String getName();

    // Default method - has implementation
    default void sayHello() {
        System.out.println("Hello, " + getName() + "!");
    }

    default void sayGoodbye() {
        System.out.println("Goodbye, " + getName() + "!");
    }
}

// Implementing class only needs to implement abstract methods
class Person implements Greeting {
    private String name;

    Person(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
    // sayHello() and sayGoodbye() inherited from interface!
}

public class BasicDefault {
    public static void main(String[] args) {
        System.out.println("=== Basic Default Methods ===\n");

        String personName = "Alice";
        Person alice = new Person(personName);

        // Calling abstract method (implemented)
        System.out.println("Name: " + alice.getName());

        // Calling default methods (inherited)
        alice.sayHello();
        alice.sayGoodbye();

        System.out.println("\n=== Another Implementation ===");

        // Anonymous class also gets defaults
        String robotName = "Robot-3000";
        Greeting robot = new Greeting() {
            @Override
            public String getName() {
                return robotName;
            }
        };

        robot.sayHello();  // Uses default

        System.out.println("\n=== Why Default Methods? ===");
        System.out.println("""
            Before Java 8:
            - Adding method to interface = breaking change
            - All implementations had to be updated

            With Default Methods:
            - Can add methods with implementation
            - Existing code continues to work
            - Interface evolution without breaking changes
            """);
    }
}
// Basic Default Method Syntax

interface Greeting {
    // Abstract method - MUST be implemented
    String getName();

    // Default method - has implementation
    default void sayHello() {
        System.out.println("Hello, " + getName() + "!");
    }

    default void sayGoodbye() {
        System.out.println("Goodbye, " + getName() + "!");
    }
}

// Implementing class only needs to implement abstract methods
class Person implements Greeting {
    private String name;

    Person(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
    // sayHello() and sayGoodbye() inherited from interface!
}

public class BasicDefault {
    public static void main(String[] args) {
        System.out.println("=== Basic Default Methods ===\n");

        String personName = "Mina";
        Person alice = new Person(personName);

        // Calling abstract method (implemented)
        System.out.println("Name: " + alice.getName());

        // Calling default methods (inherited)
        alice.sayHello();
        alice.sayGoodbye();

        System.out.println("\n=== Another Implementation ===");

        // Anonymous class also gets defaults
        String robotName = "Robot-3000";
        Greeting robot = new Greeting() {
            @Override
            public String getName() {
                return robotName;
            }
        };

        robot.sayHello();  // Uses default

        System.out.println("\n=== Why Default Methods? ===");
        System.out.println("""
            Before Java 8:
            - Adding method to interface = breaking change
            - All implementations had to be updated

            With Default Methods:
            - Can add methods with implementation
            - Existing code continues to work
            - Interface evolution without breaking changes
            """);
    }
}
// Basic Default Method Syntax

interface Greeting {
    // Abstract method - MUST be implemented
    String getName();

    // Default method - has implementation
    default void sayHello() {
        System.out.println("Hello, " + getName() + "!");
    }

    default void sayGoodbye() {
        System.out.println("Goodbye, " + getName() + "!");
    }
}

// Implementing class only needs to implement abstract methods
class Person implements Greeting {
    private String name;

    Person(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
    // sayHello() and sayGoodbye() inherited from interface!
}

public class BasicDefault {
    public static void main(String[] args) {
        System.out.println("=== Basic Default Methods ===\n");

        String personName = "Jordan";
        Person alice = new Person(personName);

        // Calling abstract method (implemented)
        System.out.println("Name: " + alice.getName());

        // Calling default methods (inherited)
        alice.sayHello();
        alice.sayGoodbye();

        System.out.println("\n=== Another Implementation ===");

        // Anonymous class also gets defaults
        String robotName = "Robot-3000";
        Greeting robot = new Greeting() {
            @Override
            public String getName() {
                return robotName;
            }
        };

        robot.sayHello();  // Uses default

        System.out.println("\n=== Why Default Methods? ===");
        System.out.println("""
            Before Java 8:
            - Adding method to interface = breaking change
            - All implementations had to be updated

            With Default Methods:
            - Can add methods with implementation
            - Existing code continues to work
            - Interface evolution without breaking changes
            """);
    }
}
// Basic Default Method Syntax

interface Greeting {
    // Abstract method - MUST be implemented
    String getName();

    // Default method - has implementation
    default void sayHello() {
        System.out.println("Hello, " + getName() + "!");
    }

    default void sayGoodbye() {
        System.out.println("Goodbye, " + getName() + "!");
    }
}

// Implementing class only needs to implement abstract methods
class Person implements Greeting {
    private String name;

    Person(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
    // sayHello() and sayGoodbye() inherited from interface!
}

public class BasicDefault {
    public static void main(String[] args) {
        System.out.println("=== Basic Default Methods ===\n");

        String personName = "Alice";
        Person alice = new Person(personName);

        // Calling abstract method (implemented)
        System.out.println("Name: " + alice.getName());

        // Calling default methods (inherited)
        alice.sayHello();
        alice.sayGoodbye();

        System.out.println("\n=== Another Implementation ===");

        // Anonymous class also gets defaults
        String robotName = "HelperBot";
        Greeting robot = new Greeting() {
            @Override
            public String getName() {
                return robotName;
            }
        };

        robot.sayHello();  // Uses default

        System.out.println("\n=== Why Default Methods? ===");
        System.out.println("""
            Before Java 8:
            - Adding method to interface = breaking change
            - All implementations had to be updated

            With Default Methods:
            - Can add methods with implementation
            - Existing code continues to work
            - Interface evolution without breaking changes
            """);
    }
}
// Basic Default Method Syntax

interface Greeting {
    // Abstract method - MUST be implemented
    String getName();

    // Default method - has implementation
    default void sayHello() {
        System.out.println("Hello, " + getName() + "!");
    }

    default void sayGoodbye() {
        System.out.println("Goodbye, " + getName() + "!");
    }
}

// Implementing class only needs to implement abstract methods
class Person implements Greeting {
    private String name;

    Person(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
    // sayHello() and sayGoodbye() inherited from interface!
}

public class BasicDefault {
    public static void main(String[] args) {
        System.out.println("=== Basic Default Methods ===\n");

        String personName = "Alice";
        Person alice = new Person(personName);

        // Calling abstract method (implemented)
        System.out.println("Name: " + alice.getName());

        // Calling default methods (inherited)
        alice.sayHello();
        alice.sayGoodbye();

        System.out.println("\n=== Another Implementation ===");

        // Anonymous class also gets defaults
        String robotName = "GuideBot";
        Greeting robot = new Greeting() {
            @Override
            public String getName() {
                return robotName;
            }
        };

        robot.sayHello();  // Uses default

        System.out.println("\n=== Why Default Methods? ===");
        System.out.println("""
            Before Java 8:
            - Adding method to interface = breaking change
            - All implementations had to be updated

            With Default Methods:
            - Can add methods with implementation
            - Existing code continues to work
            - Interface evolution without breaking changes
            """);
    }
}
  1. personName ← Alice

    32public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName→ Alice = "Alice";  //@personName="Mina", "Jordan"37        Person alice = new Person(personName);
    output=== Basic Default Methods ===
  2. this.name ← Alice

    21Person(String nameAlice) {22    this.name→ Alice = nameAlice;23}
  3. alice ← ⟨Person A⟩

    36String personName = "Alice";  //@personName="Mina", "Jordan"37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented) //?callabstract40System.out.println("Name: " + alice.getName());
  4. @Override public String getName()

    pass 1 of 3
    25@Override26public String getName() { //?mustimplement27    return nameAlice;28}
  5. System.out.println("Name: " + alice.getName());

    39// Calling abstract method (implemented) //?callabstract40System.out.println("Name: " + alice.getName());4142// Calling default methods (inherited) //?calldefault43alice.sayHello();44alice.sayGoodbye();
    outputName: Alice
  6. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() { //?defaultkeyword9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods //?implement18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() { //?mustimplement27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface! //?inherited30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";  //@personName="Mina", "Jordan"37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented) //?callabstract40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited) //?calldefault43        alice.sayHello();44        alice.sayGoodbye();
    outputHello, Alice!
  7. robotName ← Robot-3000, robot ← ⟨BasicDefault$1 B⟩

    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods //?implement18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() { //?mustimplement27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface! //?inherited30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";  //@personName="Mina", "Jordan"37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented) //?callabstract40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited) //?calldefault43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults //?anonymous49        String robotName→ Robot-3000 = "Robot-3000";  //@robotName="HelperBot", "GuideBot"50        Greeting robot→ ⟨BasicDefault$1 B⟩ = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default
    outputGoodbye, Alice!
    
    === Another Implementation ===
  8. @Override public String getName()

    50Greeting robot = new Greeting() {51    @Override52    public String getName() {53        return robotNameRobot-3000;54    }
  9. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() { //?defaultkeyword9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods //?implement18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() { //?mustimplement27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface! //?inherited30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";  //@personName="Mina", "Jordan"37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented) //?callabstract40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited) //?calldefault43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults //?anonymous49        String robotName = "Robot-3000";  //@robotName="HelperBot", "GuideBot"50        Greeting robot = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default58        59        System.out.println("\n=== Why Default Methods? ===");60        System.out.println("""61            Before Java 8:62            - Adding method to interface = breaking change63            - All implementations had to be updated64            65            With Default Methods:66            - Can add methods with implementation67            - Existing code continues to work68            - Interface evolution without breaking changes69            """);70    }
    outputHello, Robot-3000!
    
    === Why Default Methods? ===
    Before Java 8:
    - Adding method to interface = breaking change
    - All implementations had to be updated
    
    With Default Methods:
    - Can add methods with implementation
    - Existing code continues to work
    - Interface evolution without breaking changes
  1. personName ← Mina

    32public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName→ Mina = "Mina";37        Person alice = new Person(personName);
    output=== Basic Default Methods ===
  2. this.name ← Mina

    21Person(String nameMina) {22    this.name→ Mina = nameMina;23}
  3. alice ← ⟨Person A⟩

    36String personName = "Mina";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());
  4. @Override public String getName()

    pass 1 of 3
    25@Override26public String getName() {27    return nameMina;28}
  5. System.out.println("Name: " + alice.getName());

    39// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());4142// Calling default methods (inherited)43alice.sayHello();44alice.sayGoodbye();
    outputName: Mina
  6. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Mina";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();
    outputHello, Mina!
  7. robotName ← Robot-3000, robot ← ⟨BasicDefault$1 B⟩

    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Mina";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName→ Robot-3000 = "Robot-3000";50        Greeting robot→ ⟨BasicDefault$1 B⟩ = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default
    outputGoodbye, Mina!
    
    === Another Implementation ===
  8. @Override public String getName()

    50Greeting robot = new Greeting() {51    @Override52    public String getName() {53        return robotNameRobot-3000;54    }
  9. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Mina";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName = "Robot-3000";50        Greeting robot = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default58        59        System.out.println("\n=== Why Default Methods? ===");60        System.out.println("""61            Before Java 8:62            - Adding method to interface = breaking change63            - All implementations had to be updated64            65            With Default Methods:66            - Can add methods with implementation67            - Existing code continues to work68            - Interface evolution without breaking changes69            """);70    }
    outputHello, Robot-3000!
    
    === Why Default Methods? ===
    Before Java 8:
    - Adding method to interface = breaking change
    - All implementations had to be updated
    
    With Default Methods:
    - Can add methods with implementation
    - Existing code continues to work
    - Interface evolution without breaking changes
  1. personName ← Jordan

    32public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName→ Jordan = "Jordan";37        Person alice = new Person(personName);
    output=== Basic Default Methods ===
  2. this.name ← Jordan

    21Person(String nameJordan) {22    this.name→ Jordan = nameJordan;23}
  3. alice ← ⟨Person A⟩

    36String personName = "Jordan";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());
  4. @Override public String getName()

    pass 1 of 3
    25@Override26public String getName() {27    return nameJordan;28}
  5. System.out.println("Name: " + alice.getName());

    39// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());4142// Calling default methods (inherited)43alice.sayHello();44alice.sayGoodbye();
    outputName: Jordan
  6. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Jordan";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();
    outputHello, Jordan!
  7. robotName ← Robot-3000, robot ← ⟨BasicDefault$1 B⟩

    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Jordan";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName→ Robot-3000 = "Robot-3000";50        Greeting robot→ ⟨BasicDefault$1 B⟩ = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default
    outputGoodbye, Jordan!
    
    === Another Implementation ===
  8. @Override public String getName()

    50Greeting robot = new Greeting() {51    @Override52    public String getName() {53        return robotNameRobot-3000;54    }
  9. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Jordan";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName = "Robot-3000";50        Greeting robot = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default58        59        System.out.println("\n=== Why Default Methods? ===");60        System.out.println("""61            Before Java 8:62            - Adding method to interface = breaking change63            - All implementations had to be updated64            65            With Default Methods:66            - Can add methods with implementation67            - Existing code continues to work68            - Interface evolution without breaking changes69            """);70    }
    outputHello, Robot-3000!
    
    === Why Default Methods? ===
    Before Java 8:
    - Adding method to interface = breaking change
    - All implementations had to be updated
    
    With Default Methods:
    - Can add methods with implementation
    - Existing code continues to work
    - Interface evolution without breaking changes
  1. personName ← Alice

    32public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName→ Alice = "Alice";37        Person alice = new Person(personName);
    output=== Basic Default Methods ===
  2. this.name ← Alice

    21Person(String nameAlice) {22    this.name→ Alice = nameAlice;23}
  3. alice ← ⟨Person A⟩

    36String personName = "Alice";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());
  4. @Override public String getName()

    pass 1 of 3
    25@Override26public String getName() {27    return nameAlice;28}
  5. System.out.println("Name: " + alice.getName());

    39// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());4142// Calling default methods (inherited)43alice.sayHello();44alice.sayGoodbye();
    outputName: Alice
  6. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();
    outputHello, Alice!
  7. robotName ← HelperBot, robot ← ⟨BasicDefault$1 B⟩

    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName→ HelperBot = "HelperBot";50        Greeting robot→ ⟨BasicDefault$1 B⟩ = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default
    outputGoodbye, Alice!
    
    === Another Implementation ===
  8. @Override public String getName()

    50Greeting robot = new Greeting() {51    @Override52    public String getName() {53        return robotNameHelperBot;54    }
  9. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName = "HelperBot";50        Greeting robot = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default58        59        System.out.println("\n=== Why Default Methods? ===");60        System.out.println("""61            Before Java 8:62            - Adding method to interface = breaking change63            - All implementations had to be updated64            65            With Default Methods:66            - Can add methods with implementation67            - Existing code continues to work68            - Interface evolution without breaking changes69            """);70    }
    outputHello, HelperBot!
    
    === Why Default Methods? ===
    Before Java 8:
    - Adding method to interface = breaking change
    - All implementations had to be updated
    
    With Default Methods:
    - Can add methods with implementation
    - Existing code continues to work
    - Interface evolution without breaking changes
  1. personName ← Alice

    32public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName→ Alice = "Alice";37        Person alice = new Person(personName);
    output=== Basic Default Methods ===
  2. this.name ← Alice

    21Person(String nameAlice) {22    this.name→ Alice = nameAlice;23}
  3. alice ← ⟨Person A⟩

    36String personName = "Alice";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());
  4. @Override public String getName()

    pass 1 of 3
    25@Override26public String getName() {27    return nameAlice;28}
  5. System.out.println("Name: " + alice.getName());

    39// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());4142// Calling default methods (inherited)43alice.sayHello();44alice.sayGoodbye();
    outputName: Alice
  6. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();
    outputHello, Alice!
  7. robotName ← GuideBot, robot ← ⟨BasicDefault$1 B⟩

    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName→ GuideBot = "GuideBot";50        Greeting robot→ ⟨BasicDefault$1 B⟩ = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default
    outputGoodbye, Alice!
    
    === Another Implementation ===
  8. @Override public String getName()

    50Greeting robot = new Greeting() {51    @Override52    public String getName() {53        return robotNameGuideBot;54    }
  9. System.out.println("Hello, " + getName() + "!");

    8    default void sayHello() {9        System.out.println("Hello, " + getName() + "!");10    }11    12    default void sayGoodbye() {13        System.out.println("Goodbye, " + getName() + "!");14    }15}1617// Implementing class only needs to implement abstract methods18class Person implements Greeting {19    private String name;20    21    Person(String name) {22        this.name = name;23    }24    25    @Override26    public String getName() {27        return name;28    }29    // sayHello() and sayGoodbye() inherited from interface!30}3132public class BasicDefault {33    public static void main(String[] args) {34        System.out.println("=== Basic Default Methods ===\n");35        36        String personName = "Alice";37        Person alice = new Person(personName);38        39        // Calling abstract method (implemented)40        System.out.println("Name: " + alice.getName());41        42        // Calling default methods (inherited)43        alice.sayHello();44        alice.sayGoodbye();45        46        System.out.println("\n=== Another Implementation ===");47        48        // Anonymous class also gets defaults49        String robotName = "GuideBot";50        Greeting robot = new Greeting() {51            @Override52            public String getName() {53                return robotName;54            }55        };56        57        robot.sayHello();  // Uses default58        59        System.out.println("\n=== Why Default Methods? ===");60        System.out.println("""61            Before Java 8:62            - Adding method to interface = breaking change63            - All implementations had to be updated64            65            With Default Methods:66            - Can add methods with implementation67            - Existing code continues to work68            - Interface evolution without breaking changes69            """);70    }
    outputHello, GuideBot!
    
    === Why Default Methods? ===
    Before Java 8:
    - Adding method to interface = breaking change
    - All implementations had to be updated
    
    With Default Methods:
    - Can add methods with implementation
    - Existing code continues to work
    - Interface evolution without breaking changes

default methods have bodies. Implementing classes inherit the implementation.

default method Interface method with body. Implementing classes get it without writing it.

Override default methods

Implementing classes can override defaults.

OverrideDefault.java
Replay: real traced execution (multi-file project)
// Overriding Default Methods

interface Logger {
    default void log(String message) {
        System.out.println("[LOG] " + message);
    }

    default void warn(String message) {
        System.out.println("[WARN] " + message);
    }

    default void error(String message) {
        System.out.println("[ERROR] " + message);
    }
}

// Uses all defaults
class BasicLogger implements Logger {
    // No overrides - uses all default implementations
}

// Overrides some defaults
class TimestampLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[" + timestamp() + "] LOG: " + message);
    }

    @Override
    public void error(String message) {
        System.out.println("!!! [" + timestamp() + "] ERROR: " + message + " !!!");
    }

    // warn() still uses default

    private String timestamp() {
        return "09:30:00";
    }
}

// Overrides ALL defaults
class JsonLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println(format("LOG", message));
    }

    @Override
    public void warn(String message) {
        System.out.println(format("WARN", message));
    }

    @Override
    public void error(String message) {
        System.out.println(format("ERROR", message));
    }

    private String format(String level, String msg) {
        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";
    }
}

public class OverrideDefault {
    public static void main(String[] args) {
        System.out.println("=== BasicLogger (all defaults) ===");
        Logger basic = new BasicLogger();
        basic.log("Application started");
        basic.warn("Low memory");
        basic.error("Connection failed");

        System.out.println("\n=== TimestampLogger (partial override) ===");
        Logger timestamp = new TimestampLogger();
        timestamp.log("Application started");
        timestamp.warn("Low memory");  // Uses default!
        timestamp.error("Connection failed");

        System.out.println("\n=== JsonLogger (full override) ===");
        Logger json = new JsonLogger();
        json.log("Application started");
        json.warn("Low memory");
        json.error("Connection failed");

        System.out.println("\n=== Polymorphism with Defaults ===");

        Logger[] loggers = {new BasicLogger(), new TimestampLogger(), new JsonLogger()};

        for (Logger logger : loggers) {
            System.out.println("\n" + logger.getClass().getSimpleName() + ":");
            logger.log("Test message");
        }
    }
}
  1. basic ← ⟨BasicLogger A⟩

    63public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic→ ⟨BasicLogger A⟩ = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");
    output=== BasicLogger (all defaults) ===
  2. default void log(String message)

    pass 1 of 2
    3interface Logger { //?loggerinterface4    default void log(String messageApplication started) { //?defaultlog5        System.out.println("[LOG] " + messageApplication started);6    }
    output[LOG] Application started
  3. basic.log("Application started");

    66Logger basic = new BasicLogger();67basic.log("Application started");68basic.warn("Low memory");69basic.error("Connection failed");
  4. default void warn(String message)

    pass 1 of 2
    8default void warn(String messageLow memory) {9    System.out.println("[WARN] " + messageLow memory);10}
    output[WARN] Low memory
  5. basic.warn("Low memory");

    67basic.log("Application started");68basic.warn("Low memory");69basic.error("Connection failed");
  6. default void error(String message)

    12default void error(String messageConnection failed) {13    System.out.println("[ERROR] " + messageConnection failed);14}
    output[ERROR] Connection failed
  7. timestamp ← ⟨TimestampLogger B⟩

    68basic.warn("Low memory");69basic.error("Connection failed");7071System.out.println("\n=== TimestampLogger (partial override) ===");72Logger timestamp→ ⟨TimestampLogger B⟩ = new TimestampLogger();73timestamp.log("Application started");74timestamp.warn("Low memory");  // Uses default! //?partialdefault
    output
    === TimestampLogger (partial override) ===
  8. @Override public void log(String message)

    pass 1 of 2
    23class TimestampLogger implements Logger {24    @Override25    public void log(String messageApplication started) { //?overridelog26        System.out.println("[" + timestamp() + "] LOG: " + messageApplication started);27    }
  9. System.out.println("[" + timestamp() + "] LOG: " + message);

    25    public void log(String message) { //?overridelog26        System.out.println("[" + timestamp() + "] LOG: " + messageApplication started);27    }28    29    @Override30    public void error(String message) { //?overrideerror31        System.out.println("!!! [" + timestamp() + "] ERROR: " + message + " !!!");32    }33    34    // warn() still uses default35    36    private String timestamp() {37        return "09:30:00";38    }39}4041// Overrides ALL defaults //?overrideall42class JsonLogger implements Logger {43    @Override44    public void log(String message) {45        System.out.println(format("LOG", message));46    }47    48    @Override49    public void warn(String message) {50        System.out.println(format("WARN", message));51    }52    53    @Override54    public void error(String message) {55        System.out.println(format("ERROR", message));56    }57    58    private String format(String level, String msg) {59        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";60    }61}6263public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");69        basic.error("Connection failed");70        71        System.out.println("\n=== TimestampLogger (partial override) ===");72        Logger timestamp = new TimestampLogger();73        timestamp.log("Application started");74        timestamp.warn("Low memory");  // Uses default! //?partialdefault75        timestamp.error("Connection failed");
    output[09:30:00] LOG: Application started
  10. default void warn(String message)

    pass 2 of 2
    8default void warn(String messageLow memory) {9    System.out.println("[WARN] " + messageLow memory);10}
    output[WARN] Low memory
  11. timestamp.warn("Low memory"); // Uses default! //?partialdefault

    73timestamp.log("Application started");74timestamp.warn("Low memory");  // Uses default! //?partialdefault75timestamp.error("Connection failed");
  12. @Override public void error(String message)

    29@Override30public void error(String messageConnection failed) { //?overrideerror31    System.out.println("!!! [" + timestamp() + "] ERROR: " + messageConnection failed + " !!!");32}
  13. json ← ⟨JsonLogger C⟩

    30    public void error(String message) { //?overrideerror31        System.out.println("!!! [" + timestamp() + "] ERROR: " + messageConnection failed + " !!!");32    }33    34    // warn() still uses default35    36    private String timestamp() {37        return "09:30:00";38    }39}4041// Overrides ALL defaults //?overrideall42class JsonLogger implements Logger {43    @Override44    public void log(String message) {45        System.out.println(format("LOG", message));46    }47    48    @Override49    public void warn(String message) {50        System.out.println(format("WARN", message));51    }52    53    @Override54    public void error(String message) {55        System.out.println(format("ERROR", message));56    }57    58    private String format(String level, String msg) {59        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";60    }61}6263public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");69        basic.error("Connection failed");70        71        System.out.println("\n=== TimestampLogger (partial override) ===");72        Logger timestamp = new TimestampLogger();73        timestamp.log("Application started");74        timestamp.warn("Low memory");  // Uses default! //?partialdefault75        timestamp.error("Connection failed");76        77        System.out.println("\n=== JsonLogger (full override) ===");78        Logger json→ ⟨JsonLogger C⟩ = new JsonLogger();79        json.log("Application started");80        json.warn("Low memory");
    output!!! [09:30:00] ERROR: Connection failed !!!
    
    === JsonLogger (full override) ===
  14. @Override public void log(String message)

    pass 1 of 2
    42class JsonLogger implements Logger {43    @Override44    public void log(String messageApplication started) {45        System.out.println(format("LOG", messageApplication started));46    }
  15. private String format(String level, String msg)

    pass 1 of 4
    58private String format(String levelLOG, String msgApplication started) {59    return "{\"level\":\"" + levelLOG + "\",\"message\":\"" + msgApplication started + "\"}";60}
    All 4 passes — pass 1 is the card above
    passlevelmsg
    1LOGApplication started
    2WARNLow memory
    3ERRORConnection failed
    4LOGTest message
  16. System.out.println(format("LOG", message));

    44    public void log(String message) {45        System.out.println(format("LOG", messageApplication started));46    }47    48    @Override49    public void warn(String message) {50        System.out.println(format("WARN", message));51    }52    53    @Override54    public void error(String message) {55        System.out.println(format("ERROR", message));56    }57    58    private String format(String level, String msg) {59        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";60    }61}6263public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");69        basic.error("Connection failed");70        71        System.out.println("\n=== TimestampLogger (partial override) ===");72        Logger timestamp = new TimestampLogger();73        timestamp.log("Application started");74        timestamp.warn("Low memory");  // Uses default! //?partialdefault75        timestamp.error("Connection failed");76        77        System.out.println("\n=== JsonLogger (full override) ===");78        Logger json = new JsonLogger();79        json.log("Application started");80        json.warn("Low memory");81        json.error("Connection failed");
    output{"level":"LOG","message":"Application started"}
  17. @Override public void warn(String message)

    48@Override49public void warn(String messageLow memory) {50    System.out.println(format("WARN", messageLow memory));51}
  18. System.out.println(format("WARN", message));

    49    public void warn(String message) {50        System.out.println(format("WARN", messageLow memory));51    }52    53    @Override54    public void error(String message) {55        System.out.println(format("ERROR", message));56    }57    58    private String format(String level, String msg) {59        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";60    }61}6263public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");69        basic.error("Connection failed");70        71        System.out.println("\n=== TimestampLogger (partial override) ===");72        Logger timestamp = new TimestampLogger();73        timestamp.log("Application started");74        timestamp.warn("Low memory");  // Uses default! //?partialdefault75        timestamp.error("Connection failed");76        77        System.out.println("\n=== JsonLogger (full override) ===");78        Logger json = new JsonLogger();79        json.log("Application started");80        json.warn("Low memory");81        json.error("Connection failed");
    output{"level":"WARN","message":"Low memory"}
  19. @Override public void error(String message)

    53@Override54public void error(String messageConnection failed) {55    System.out.println(format("ERROR", messageConnection failed));56}
  20. System.out.println(format("ERROR", message));

    54    public void error(String message) {55        System.out.println(format("ERROR", messageConnection failed));56    }57    58    private String format(String level, String msg) {59        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";60    }61}6263public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");69        basic.error("Connection failed");70        71        System.out.println("\n=== TimestampLogger (partial override) ===");72        Logger timestamp = new TimestampLogger();73        timestamp.log("Application started");74        timestamp.warn("Low memory");  // Uses default! //?partialdefault75        timestamp.error("Connection failed");76        77        System.out.println("\n=== JsonLogger (full override) ===");78        Logger json = new JsonLogger();79        json.log("Application started");80        json.warn("Low memory");81        json.error("Connection failed");82        83        System.out.println("\n=== Polymorphism with Defaults ===");84        85        Logger[] loggers = {new BasicLogger(), new TimestampLogger(), new JsonLogger()};
    output{"level":"ERROR","message":"Connection failed"}
    
    === Polymorphism with Defaults ===
  21. for (Logger logger : loggers)

    pass 1 of 3
    87for (Logger logger⟨BasicLogger D⟩ : loggers) { //?polymorphic88    System.out.println("\n" + logger.getClass().getSimpleName() + ":");89    logger.log("Test message");90}
    output
    BasicLogger:
    All 3 passes — pass 1 is the card above
    passlogger
    1⟨BasicLogger D⟩
    2⟨TimestampLogger E⟩
    3⟨JsonLogger F⟩
  22. default void log(String message)

    pass 2 of 2
    3interface Logger { //?loggerinterface4    default void log(String messageTest message) { //?defaultlog5        System.out.println("[LOG] " + messageTest message);6    }
    output[LOG] Test message
  23. logger.log("Test message");

    88    System.out.println("\n" + logger.getClass().getSimpleName() + ":");89    logger.log("Test message");90}
  24. @Override public void log(String message)

    pass 2 of 2
    23class TimestampLogger implements Logger {24    @Override25    public void log(String messageTest message) { //?overridelog26        System.out.println("[" + timestamp() + "] LOG: " + messageTest message);27    }
  25. System.out.println("[" + timestamp() + "] LOG: " + message);

    25    public void log(String message) { //?overridelog26        System.out.println("[" + timestamp() + "] LOG: " + messageTest message);27    }28    29    @Override30    public void error(String message) { //?overrideerror31        System.out.println("!!! [" + timestamp() + "] ERROR: " + message + " !!!");32    }33    34    // warn() still uses default35    36    private String timestamp() {37        return "09:30:00";38    }39}4041// Overrides ALL defaults //?overrideall42class JsonLogger implements Logger {43    @Override44    public void log(String message) {45        System.out.println(format("LOG", message));46    }47    48    @Override49    public void warn(String message) {50        System.out.println(format("WARN", message));51    }52    53    @Override54    public void error(String message) {55        System.out.println(format("ERROR", message));56    }57    58    private String format(String level, String msg) {59        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";60    }61}6263public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");69        basic.error("Connection failed");70        71        System.out.println("\n=== TimestampLogger (partial override) ===");72        Logger timestamp = new TimestampLogger();73        timestamp.log("Application started");74        timestamp.warn("Low memory");  // Uses default! //?partialdefault75        timestamp.error("Connection failed");76        77        System.out.println("\n=== JsonLogger (full override) ===");78        Logger json = new JsonLogger();79        json.log("Application started");80        json.warn("Low memory");81        json.error("Connection failed");82        83        System.out.println("\n=== Polymorphism with Defaults ===");84        85        Logger[] loggers = {new BasicLogger(), new TimestampLogger(), new JsonLogger()};86        87        for (Logger logger : loggers) { //?polymorphic88            System.out.println("\n" + logger.getClass().getSimpleName() + ":");89            logger.log("Test message");90        }
    output[09:30:00] LOG: Test message
  26. @Override public void log(String message)

    pass 2 of 2
    42class JsonLogger implements Logger {43    @Override44    public void log(String messageTest message) {45        System.out.println(format("LOG", messageTest message));46    }
  27. System.out.println(format("LOG", message));

    44    public void log(String message) {45        System.out.println(format("LOG", messageTest message));46    }47    48    @Override49    public void warn(String message) {50        System.out.println(format("WARN", message));51    }52    53    @Override54    public void error(String message) {55        System.out.println(format("ERROR", message));56    }57    58    private String format(String level, String msg) {59        return "{\"level\":\"" + level + "\",\"message\":\"" + msg + "\"}";60    }61}6263public class OverrideDefault {64    public static void main(String[] args) {65        System.out.println("=== BasicLogger (all defaults) ===");66        Logger basic = new BasicLogger();67        basic.log("Application started");68        basic.warn("Low memory");69        basic.error("Connection failed");70        71        System.out.println("\n=== TimestampLogger (partial override) ===");72        Logger timestamp = new TimestampLogger();73        timestamp.log("Application started");74        timestamp.warn("Low memory");  // Uses default! //?partialdefault75        timestamp.error("Connection failed");76        77        System.out.println("\n=== JsonLogger (full override) ===");78        Logger json = new JsonLogger();79        json.log("Application started");80        json.warn("Low memory");81        json.error("Connection failed");82        83        System.out.println("\n=== Polymorphism with Defaults ===");84        85        Logger[] loggers = {new BasicLogger(), new TimestampLogger(), new JsonLogger()};86        87        for (Logger logger : loggers) { //?polymorphic88            System.out.println("\n" + logger.getClass().getSimpleName() + ":");89            logger.log("Test message");90        }
    output{"level":"LOG","message":"Test message"}

Classes can accept the default or provide their own implementation.

Multiple interfaces with defaults

Handle conflicts when interfaces have same default method.

MultipleDefaults.java
Replay: real traced execution (multi-file project)
// Multiple Interfaces with Default Methods

interface Flyable {
    default void move() {
        System.out.println("Flying through the air");
    }

    default void takeOff() {
        System.out.println("Taking off...");
    }
}

interface Swimmable {
    default void move() {
        System.out.println("Swimming through the water");
    }

    default void dive() {
        System.out.println("Diving deep...");
    }
}

// Implements only Flyable - no conflict
class Bird implements Flyable {
    // Gets Flyable.move() and takeOff()
}

// Implements only Swimmable - no conflict
class Fish implements Swimmable {
    // Gets Swimmable.move() and dive()
}

// Implements BOTH - MUST resolve conflict!
class Duck implements Flyable, Swimmable {

    @Override
    public void move() {
        // MUST override - compiler error otherwise!
        System.out.println("Duck can fly and swim!");
    }

    // Can call specific interface's default
    public void flyMove() {
        Flyable.super.move();  // Calls Flyable's default
    }

    public void swimMove() {
        Swimmable.super.move();  // Calls Swimmable's default
    }

    // takeOff() from Flyable - no conflict
    // dive() from Swimmable - no conflict
}

// Another approach: delegate to one interface
class Seaplane implements Flyable, Swimmable {
    @Override
    public void move() {
        // Delegate to Flyable's version
        Flyable.super.move();
    }
}

public class MultipleDefaults {
    public static void main(String[] args) {
        System.out.println("=== Single Interface ===");
        Bird bird = new Bird();
        bird.move();
        bird.takeOff();

        Fish fish = new Fish();
        fish.move();
        fish.dive();

        System.out.println("\n=== Diamond Problem Resolution ===");
        Duck duck = new Duck();
        duck.move();  // Duck's override
        duck.takeOff();  // From Flyable
        duck.dive();  // From Swimmable

        System.out.println("\n=== Calling Specific Interface's Default ===");
        duck.flyMove();
        duck.swimMove();

        System.out.println("\n=== Delegation Approach ===");
        Seaplane plane = new Seaplane();
        plane.move();  // Delegates to Flyable

        System.out.println("\n=== Diamond Problem Rules ===");
        System.out.println("""
            When two interfaces have same default method:
            1. Implementing class MUST override
            2. Compiler error if not resolved
            3. Can call specific default: Interface.super.method()

            No conflict if:
            - Methods have different names
            - One interface extends the other
            """);
    }
}
  1. bird ← ⟨Bird A⟩

    64public class MultipleDefaults {65    public static void main(String[] args) {66        System.out.println("=== Single Interface ===");67        Bird bird→ ⟨Bird A⟩ = new Bird();68        bird.move();69        bird.takeOff();
    output=== Single Interface ===
  2. default void move()

    pass 1 of 3
    3interface Flyable { //?flyable4    default void move() { //?flymove5        System.out.println("Flying through the air");6    }
    outputFlying through the air
  3. bird.move();

    67Bird bird = new Bird();68bird.move();69bird.takeOff();
  4. default void takeOff()

    pass 1 of 2
    8default void takeOff() {9    System.out.println("Taking off...");10}
    outputTaking off...
  5. fish ← ⟨Fish B⟩

    68bird.move();69bird.takeOff();7071Fish fish→ ⟨Fish B⟩ = new Fish();72fish.move();73fish.dive();
  6. default void move()

    pass 1 of 2
    13interface Swimmable { //?swimmable14    default void move() { //?swimmove15        System.out.println("Swimming through the water");16    }
    outputSwimming through the water
  7. fish.move();

    71Fish fish = new Fish();72fish.move();73fish.dive();
  8. default void dive()

    pass 1 of 2
    18default void dive() {19    System.out.println("Diving deep...");20}
    outputDiving deep...
  9. duck ← ⟨Duck C⟩

    72fish.move();73fish.dive();7475System.out.println("\n=== Diamond Problem Resolution ===");76Duck duck→ ⟨Duck C⟩ = new Duck();77duck.move();  // Duck's override78duck.takeOff();  // From Flyable
    output
    === Diamond Problem Resolution ===
  10. @Override public void move()

    36@Override37public void move() { //?mustoverride38    // MUST override - compiler error otherwise!39    System.out.println("Duck can fly and swim!");40}
    outputDuck can fly and swim!
  11. duck.move(); // Duck's override

    76Duck duck = new Duck();77duck.move();  // Duck's override78duck.takeOff();  // From Flyable79duck.dive();  // From Swimmable
  12. default void takeOff()

    pass 2 of 2
    8default void takeOff() {9    System.out.println("Taking off...");10}
    outputTaking off...
  13. duck.takeOff(); // From Flyable

    77duck.move();  // Duck's override78duck.takeOff();  // From Flyable79duck.dive();  // From Swimmable
  14. default void dive()

    pass 2 of 2
    18default void dive() {19    System.out.println("Diving deep...");20}
    outputDiving deep...
  15. duck.dive(); // From Swimmable

    78duck.takeOff();  // From Flyable79duck.dive();  // From Swimmable8081System.out.println("\n=== Calling Specific Interface's Default ===");82duck.flyMove();83duck.swimMove();
    output
    === Calling Specific Interface's Default ===
  16. Flyable.super.move(); // Calls Flyable's default

    43public void flyMove() {44    Flyable.super.move();  // Calls Flyable's default45}
  17. duck.flyMove();

    81System.out.println("\n=== Calling Specific Interface's Default ===");82duck.flyMove();83duck.swimMove();
  18. default void move()

    pass 2 of 2
    13interface Swimmable { //?swimmable14    default void move() { //?swimmove15        System.out.println("Swimming through the water");16    }
    outputSwimming through the water
  19. Swimmable.super.move(); // Calls Swimmable's default

    47public void swimMove() {48    Swimmable.super.move();  // Calls Swimmable's default49}
  20. plane ← ⟨Seaplane D⟩

    82duck.flyMove();83duck.swimMove();8485System.out.println("\n=== Delegation Approach ===");86Seaplane plane→ ⟨Seaplane D⟩ = new Seaplane();87plane.move();  // Delegates to Flyable
    output
    === Delegation Approach ===
  21. Flyable.super.move(); //?delegateflyable

    59    // Delegate to Flyable's version60    Flyable.super.move(); //?delegateflyable61}
  22. plane.move(); // Delegates to Flyable

    86    Seaplane plane = new Seaplane();87    plane.move();  // Delegates to Flyable88    89    System.out.println("\n=== Diamond Problem Rules ===");90    System.out.println("""91        When two interfaces have same default method:92        1. Implementing class MUST override93        2. Compiler error if not resolved94        3. Can call specific default: Interface.super.method()95        96        No conflict if:97        - Methods have different names98        - One interface extends the other99        """);100}
    output
    === Diamond Problem Rules ===
    When two interfaces have same default method:
    1. Implementing class MUST override
    2. Compiler error if not resolved
    3. Can call specific default: Interface.super.method()
    
    No conflict if:
    - Methods have different names
    - One interface extends the other

Class must override if two interfaces have same default method signature.

diamond problem Conflict when class inherits same method from multiple interfaces. Must resolve explicitly.

Default calls abstract

Default methods can use abstract methods.

DefaultWithAbstract.java
Replay: real traced execution (multi-file project)
// Combining Default and Abstract Methods

interface DataProcessor {
    // Abstract methods - MUST implement
    String getSource();
    void processItem(String item);

    // Default methods - use abstract methods
    default void processAll(String[] items) {
        System.out.println("Processing from: " + getSource());
        for (String item : items) {
            processItem(item);  // Calls abstract method
        }
        System.out.println("Done processing " + items.length + " items");
    }

    default void validate(String item) {
        if (item == null || item.isEmpty()) {
            throw new IllegalArgumentException("Invalid item");
        }
    }
}

// Implementation must provide abstract methods
class FileProcessor implements DataProcessor {
    private String filename;

    FileProcessor(String filename) {
        this.filename = filename;
    }

    @Override
    public String getSource() {
        return "File: " + filename;
    }

    @Override
    public void processItem(String item) {
        validate(item);  // Uses default!
        System.out.println("  Processing: " + item.toUpperCase());
    }
}

class DatabaseProcessor implements DataProcessor {
    private String tableName;

    DatabaseProcessor(String tableName) {
        this.tableName = tableName;
    }

    @Override
    public String getSource() {
        return "Database: " + tableName;
    }

    @Override
    public void processItem(String item) {
        validate(item);
        System.out.println("  INSERT INTO " + tableName + " VALUES ('" + item + "')");
    }
}

// Interface with hook methods
interface Workflow {
    // Template with hooks
    default void execute() {
        beforeStart();  // Hook
        doWork();       // Abstract - must implement
        afterComplete(); // Hook
    }

    // Hooks with empty defaults
    default void beforeStart() {
        // Empty default - override to add behavior
    }

    default void afterComplete() {
        // Empty default - override to add behavior
    }

    // Abstract - must implement
    void doWork();
}

class SimpleTask implements Workflow {
    @Override
    public void doWork() {
        System.out.println("Doing simple work...");
    }
    // Uses empty hooks
}

class LoggedTask implements Workflow {
    @Override
    public void doWork() {
        System.out.println("Doing logged work...");
    }

    @Override
    public void beforeStart() {
        System.out.println("[LOG] Task starting...");
    }

    @Override
    public void afterComplete() {
        System.out.println("[LOG] Task completed!");
    }
}

public class DefaultWithAbstract {
    public static void main(String[] args) {
        System.out.println("=== DataProcessor ===\n");

        String[] data = {"apple", "banana", "cherry"};

        DataProcessor fileProc = new FileProcessor("data.txt");
        fileProc.processAll(data);

        System.out.println();

        DataProcessor dbProc = new DatabaseProcessor("fruits");
        dbProc.processAll(data);

        System.out.println("\n=== Workflow with Hooks ===\n");

        System.out.println("SimpleTask:");
        Workflow simple = new SimpleTask();
        simple.execute();

        System.out.println("\nLoggedTask:");
        Workflow logged = new LoggedTask();
        logged.execute();

        System.out.println("\n=== Pattern Summary ===");
        System.out.println("""
            Template Method in Interfaces:
            - Default method defines algorithm structure
            - Abstract methods are customization points
            - Implementations provide specific behavior

            Hook Pattern:
            - Default methods with empty bodies
            - Override to add optional behavior
            - Clean extension points
            """);
    }
}
  1. public static void main(String[] args)

    110public class DefaultWithAbstract {111    public static void main(String[] args) {112        System.out.println("=== DataProcessor ===\n");113        114        String[] data = {"apple", "banana", "cherry"};115        116        DataProcessor fileProc = new FileProcessor("data.txt");117        fileProc.processAll(data);
    output=== DataProcessor ===
  2. this.filename ← data.txt

    28FileProcessor(String filenamedata.txt) {29    this.filename→ data.txt = filenamedata.txt;30}
  3. fileProc ← ⟨FileProcessor A⟩

    116DataProcessor fileProc→ ⟨FileProcessor A⟩ = new FileProcessor("data.txt");117fileProc.processAll(data);
  4. default void processAll(String[] items)

    pass 1 of 2
    8// Default methods - use abstract methods //?defaultuseabstract9default void processAll(String[] items) { //?templatemethod10    System.out.println("Processing from: " + getSource());11    for (String item : items) {
  5. @Override public String getSource()

    32@Override33public String getSource() { //?implementsource34    return "File: " + filenamedata.txt;35}
  6. System.out.println("Processing from: " + getSource());

    9default void processAll(String[] items) { //?templatemethod10    System.out.println("Processing from: " + getSource());11    for (String item : items) {
    outputProcessing from: File: data.txt
  7. for (String item : items)

    pass 1 of 6
    10System.out.println("Processing from: " + getSource());11for (String itemapple : items) {12    processItem(itemapple);  // Calls abstract method //?callabstract13}
    All 6 passes — pass 1 is the card above
    passitem
    1apple
    2banana
    3cherry
    4apple
    5banana
    6cherry
  8. @Override public void processItem(String item)

    pass 1 of 3
    37@Override38public void processItem(String itemapple) { //?implementprocess39    validate(itemapple);  // Uses default! //?usedefault40    System.out.println("  Processing: " + item.toUpperCase());
    All 3 passes — pass 1 is the card above
    passitem
    1apple
    2banana
    3cherry
  9. default void validate(String item)

    pass 1 of 6
    17default void validate(String itemapple) {18    if (item == null || item.isEmpty()) {
    All 6 passes — pass 1 is the card above
    passitem
    1apple
    2banana
    3cherry
    4apple
    5banana
    6cherry
  10. validate(item); // Uses default! //?usedefault

    11        for (String item : items) {12            processItem(itemapple);  // Calls abstract method //?callabstract13        }14        System.out.println("Done processing " + items.length + " items");15    }16    17    default void validate(String item) {18        if (item == null || item.isEmpty()) {19            throw new IllegalArgumentException("Invalid item");20        }21    }22}2324// Implementation must provide abstract methods //?implementation25class FileProcessor implements DataProcessor {26    private String filename;27    28    FileProcessor(String filename) {29        this.filename = filename;30    }31    32    @Override33    public String getSource() { //?implementsource34        return "File: " + filename;35    }36    37    @Override38    public void processItem(String item) { //?implementprocess39        validate(itemapple);  // Uses default! //?usedefault40        System.out.println("  Processing: " + item.toUpperCase());41    }
    output  Processing: APPLE
  11. validate(item); // Uses default! //?usedefault

    11        for (String item : items) {12            processItem(itembanana);  // Calls abstract method //?callabstract13        }14        System.out.println("Done processing " + items.length + " items");15    }16    17    default void validate(String item) {18        if (item == null || item.isEmpty()) {19            throw new IllegalArgumentException("Invalid item");20        }21    }22}2324// Implementation must provide abstract methods //?implementation25class FileProcessor implements DataProcessor {26    private String filename;27    28    FileProcessor(String filename) {29        this.filename = filename;30    }31    32    @Override33    public String getSource() { //?implementsource34        return "File: " + filename;35    }36    37    @Override38    public void processItem(String item) { //?implementprocess39        validate(itembanana);  // Uses default! //?usedefault40        System.out.println("  Processing: " + item.toUpperCase());41    }
    output  Processing: BANANA
  12. validate(item); // Uses default! //?usedefault

    11        for (String item : items) {12            processItem(itemcherry);  // Calls abstract method //?callabstract13        }14        System.out.println("Done processing " + items.length3 + " items");15    }16    17    default void validate(String item) {18        if (item == null || item.isEmpty()) {19            throw new IllegalArgumentException("Invalid item");20        }21    }22}2324// Implementation must provide abstract methods //?implementation25class FileProcessor implements DataProcessor {26    private String filename;27    28    FileProcessor(String filename) {29        this.filename = filename;30    }31    32    @Override33    public String getSource() { //?implementsource34        return "File: " + filename;35    }36    37    @Override38    public void processItem(String item) { //?implementprocess39        validate(itemcherry);  // Uses default! //?usedefault40        System.out.println("  Processing: " + item.toUpperCase());41    }
    output  Processing: CHERRY
    Done processing 3 items
  13. fileProc.processAll(data);

    116DataProcessor fileProc = new FileProcessor("data.txt");117fileProc.processAll(data);118119System.out.println();120121DataProcessor dbProc = new DatabaseProcessor("fruits");122dbProc.processAll(data);
  14. this.tableName ← fruits

    47DatabaseProcessor(String tableNamefruits) {48    this.tableName→ fruits = tableNamefruits;49}
  15. dbProc ← ⟨DatabaseProcessor B⟩

    121DataProcessor dbProc→ ⟨DatabaseProcessor B⟩ = new DatabaseProcessor("fruits");122dbProc.processAll(data);
  16. default void processAll(String[] items)

    pass 2 of 2
    8// Default methods - use abstract methods //?defaultuseabstract9default void processAll(String[] items) { //?templatemethod10    System.out.println("Processing from: " + getSource());11    for (String item : items) {
  17. @Override public String getSource()

    51@Override52public String getSource() {53    return "Database: " + tableNamefruits;54}
  18. System.out.println("Processing from: " + getSource());

    9default void processAll(String[] items) { //?templatemethod10    System.out.println("Processing from: " + getSource());11    for (String item : items) {
    outputProcessing from: Database: fruits
  19. @Override public void processItem(String item)

    pass 1 of 3
    56@Override57public void processItem(String itemapple) {58    validate(itemapple);59    System.out.println("  INSERT INTO " + tableName + " VALUES ('" + item + "')");
    All 3 passes — pass 1 is the card above
    passitem
    1apple
    2banana
    3cherry
  20. validate(item);

    11        for (String item : items) {12            processItem(itemapple);  // Calls abstract method //?callabstract13        }14        System.out.println("Done processing " + items.length + " items");15    }16    17    default void validate(String item) {18        if (item == null || item.isEmpty()) {19            throw new IllegalArgumentException("Invalid item");20        }21    }22}2324// Implementation must provide abstract methods //?implementation25class FileProcessor implements DataProcessor {26    private String filename;27    28    FileProcessor(String filename) {29        this.filename = filename;30    }31    32    @Override33    public String getSource() { //?implementsource34        return "File: " + filename;35    }36    37    @Override38    public void processItem(String item) { //?implementprocess39        validate(item);  // Uses default! //?usedefault40        System.out.println("  Processing: " + item.toUpperCase());41    }42}4344class DatabaseProcessor implements DataProcessor {45    private String tableName;46    47    DatabaseProcessor(String tableName) {48        this.tableName = tableName;49    }50    51    @Override52    public String getSource() {53        return "Database: " + tableName;54    }55    56    @Override57    public void processItem(String item) {58        validate(itemapple);59        System.out.println("  INSERT INTO " + tableNamefruits + " VALUES ('" + itemapple + "')");60    }
    output  INSERT INTO fruits VALUES ('apple')
  21. validate(item);

    11        for (String item : items) {12            processItem(itembanana);  // Calls abstract method //?callabstract13        }14        System.out.println("Done processing " + items.length + " items");15    }16    17    default void validate(String item) {18        if (item == null || item.isEmpty()) {19            throw new IllegalArgumentException("Invalid item");20        }21    }22}2324// Implementation must provide abstract methods //?implementation25class FileProcessor implements DataProcessor {26    private String filename;27    28    FileProcessor(String filename) {29        this.filename = filename;30    }31    32    @Override33    public String getSource() { //?implementsource34        return "File: " + filename;35    }36    37    @Override38    public void processItem(String item) { //?implementprocess39        validate(item);  // Uses default! //?usedefault40        System.out.println("  Processing: " + item.toUpperCase());41    }42}4344class DatabaseProcessor implements DataProcessor {45    private String tableName;46    47    DatabaseProcessor(String tableName) {48        this.tableName = tableName;49    }50    51    @Override52    public String getSource() {53        return "Database: " + tableName;54    }55    56    @Override57    public void processItem(String item) {58        validate(itembanana);59        System.out.println("  INSERT INTO " + tableNamefruits + " VALUES ('" + itembanana + "')");60    }
    output  INSERT INTO fruits VALUES ('banana')
  22. validate(item);

    11        for (String item : items) {12            processItem(itemcherry);  // Calls abstract method //?callabstract13        }14        System.out.println("Done processing " + items.length3 + " items");15    }16    17    default void validate(String item) {18        if (item == null || item.isEmpty()) {19            throw new IllegalArgumentException("Invalid item");20        }21    }22}2324// Implementation must provide abstract methods //?implementation25class FileProcessor implements DataProcessor {26    private String filename;27    28    FileProcessor(String filename) {29        this.filename = filename;30    }31    32    @Override33    public String getSource() { //?implementsource34        return "File: " + filename;35    }36    37    @Override38    public void processItem(String item) { //?implementprocess39        validate(item);  // Uses default! //?usedefault40        System.out.println("  Processing: " + item.toUpperCase());41    }42}4344class DatabaseProcessor implements DataProcessor {45    private String tableName;46    47    DatabaseProcessor(String tableName) {48        this.tableName = tableName;49    }50    51    @Override52    public String getSource() {53        return "Database: " + tableName;54    }55    56    @Override57    public void processItem(String item) {58        validate(itemcherry);59        System.out.println("  INSERT INTO " + tableNamefruits + " VALUES ('" + itemcherry + "')");60    }
    output  INSERT INTO fruits VALUES ('cherry')
    Done processing 3 items
  23. simple ← ⟨SimpleTask C⟩

    121DataProcessor dbProc = new DatabaseProcessor("fruits");122dbProc.processAll(data);123124System.out.println("\n=== Workflow with Hooks ===\n");125126System.out.println("SimpleTask:");127Workflow simple→ ⟨SimpleTask C⟩ = new SimpleTask();128simple.execute();
    output
    === Workflow with Hooks ===
    SimpleTask:
  24. @Override public void doWork()

    67        beforeStart();  // Hook68        doWork();       // Abstract - must implement69        afterComplete(); // Hook70    }71    72    // Hooks with empty defaults //?hooks73    default void beforeStart() {74        // Empty default - override to add behavior75    }76    77    default void afterComplete() {78        // Empty default - override to add behavior79    }80    81    // Abstract - must implement82    void doWork();83}8485class SimpleTask implements Workflow {86    @Override87    public void doWork() { //?simpletask88        System.out.println("Doing simple work...");89    }
    outputDoing simple work...
  25. logged ← ⟨LoggedTask D⟩

    127Workflow simple = new SimpleTask();128simple.execute();129130System.out.println("\nLoggedTask:");131Workflow logged→ ⟨LoggedTask D⟩ = new LoggedTask();132logged.execute();
    output
    LoggedTask:
  26. @Override public void beforeStart()

    66    default void execute() { //?templateexecute67        beforeStart();  // Hook68        doWork();       // Abstract - must implement69        afterComplete(); // Hook70    }71    72    // Hooks with empty defaults //?hooks73    default void beforeStart() {74        // Empty default - override to add behavior75    }76    77    default void afterComplete() {78        // Empty default - override to add behavior79    }80    81    // Abstract - must implement82    void doWork();83}8485class SimpleTask implements Workflow {86    @Override87    public void doWork() { //?simpletask88        System.out.println("Doing simple work...");89    }90    // Uses empty hooks91}9293class LoggedTask implements Workflow {94    @Override95    public void doWork() {96        System.out.println("Doing logged work...");97    }98    99    @Override100    public void beforeStart() { //?overridehook101        System.out.println("[LOG] Task starting...");102    }
    output[LOG] Task starting...
  27. @Override public void doWork()

    67        beforeStart();  // Hook68        doWork();       // Abstract - must implement69        afterComplete(); // Hook70    }71    72    // Hooks with empty defaults //?hooks73    default void beforeStart() {74        // Empty default - override to add behavior75    }76    77    default void afterComplete() {78        // Empty default - override to add behavior79    }80    81    // Abstract - must implement82    void doWork();83}8485class SimpleTask implements Workflow {86    @Override87    public void doWork() { //?simpletask88        System.out.println("Doing simple work...");89    }90    // Uses empty hooks91}9293class LoggedTask implements Workflow {94    @Override95    public void doWork() {96        System.out.println("Doing logged work...");97    }
    outputDoing logged work...
  28. @Override public void afterComplete()

    68        doWork();       // Abstract - must implement69        afterComplete(); // Hook70    }71    72    // Hooks with empty defaults //?hooks73    default void beforeStart() {74        // Empty default - override to add behavior75    }76    77    default void afterComplete() {78        // Empty default - override to add behavior79    }80    81    // Abstract - must implement82    void doWork();83}8485class SimpleTask implements Workflow {86    @Override87    public void doWork() { //?simpletask88        System.out.println("Doing simple work...");89    }90    // Uses empty hooks91}9293class LoggedTask implements Workflow {94    @Override95    public void doWork() {96        System.out.println("Doing logged work...");97    }98    99    @Override100    public void beforeStart() { //?overridehook101        System.out.println("[LOG] Task starting...");102    }103    104    @Override105    public void afterComplete() {106        System.out.println("[LOG] Task completed!");107    }
    output[LOG] Task completed!
  29. logged.execute();

    131    Workflow logged = new LoggedTask();132    logged.execute();133    134    System.out.println("\n=== Pattern Summary ===");135    System.out.println("""136        Template Method in Interfaces:137        - Default method defines algorithm structure138        - Abstract methods are customization points139        - Implementations provide specific behavior140        141        Hook Pattern:142        - Default methods with empty bodies143        - Override to add optional behavior144        - Clean extension points145        """);146}
    output
    === Pattern Summary ===
    Template Method in Interfaces:
    - Default method defines algorithm structure
    - Abstract methods are customization points
    - Implementations provide specific behavior
    
    Hook Pattern:
    - Default methods with empty bodies
    - Override to add optional behavior
    - Clean extension points

Default implementation calls abstract methods that subclasses implement.

API evolution

Add new methods to existing interfaces safely.

RealWorldEvolution.java
Replay: real traced execution (multi-file project)
// Using Default Methods for API Evolution

// Version 1 of the API (before default methods)
interface PaymentProcessorV1 {
    boolean processPayment(double amount);
    boolean refund(String transactionId);
}

// Old implementation works with V1
class StripePaymentV1 implements PaymentProcessorV1 {
    @Override
    public boolean processPayment(double amount) {
        System.out.println("Stripe: Processing $" + amount);
        return true;
    }

    @Override
    public boolean refund(String transactionId) {
        System.out.println("Stripe: Refunding " + transactionId);
        return true;
    }
}

// Version 2 - Adding new features with defaults
interface PaymentProcessorV2 {
    boolean processPayment(double amount);
    boolean refund(String transactionId);

    // NEW in V2 - won't break existing implementations!
    default boolean processRecurring(double amount, String schedule) {
        System.out.println("Default recurring: $" + amount + " " + schedule);
        return processPayment(amount);  // Fallback to regular payment
    }

    // NEW in V2
    default void validatePaymentMethod() {
        System.out.println("Default validation passed");
    }

    // NEW in V2 - utility method
    default String formatAmount(double amount) {
        return String.format("$%.2f", amount);
    }
}

// Old implementation upgraded to V2 - still works!
class StripePaymentV2 implements PaymentProcessorV2 {
    @Override
    public boolean processPayment(double amount) {
        System.out.println("Stripe: Processing " + formatAmount(amount));
        return true;
    }

    @Override
    public boolean refund(String transactionId) {
        System.out.println("Stripe: Refunding " + transactionId);
        return true;
    }
    // processRecurring() - uses default
    // validatePaymentMethod() - uses default
}

// New implementation can override new methods
class PayPalPaymentV2 implements PaymentProcessorV2 {
    @Override
    public boolean processPayment(double amount) {
        System.out.println("PayPal: Processing " + formatAmount(amount));
        return true;
    }

    @Override
    public boolean refund(String transactionId) {
        System.out.println("PayPal: Refunding " + transactionId);
        return true;
    }

    @Override
    public boolean processRecurring(double amount, String schedule) {
        System.out.println("PayPal Subscriptions: " + formatAmount(amount) +
                          " billed " + schedule);
        return true;
    }

    @Override
    public void validatePaymentMethod() {
        System.out.println("PayPal: Validating linked account...");
    }
}

public class RealWorldEvolution {
    public static void main(String[] args) {
        System.out.println("=== API Evolution Example ===\n");

        System.out.println("--- Stripe (uses defaults) ---");
        PaymentProcessorV2 stripe = new StripePaymentV2();
        stripe.processPayment(99.99);
        stripe.validatePaymentMethod();  // Uses default
        stripe.processRecurring(9.99, "monthly");  // Uses default

        System.out.println("\n--- PayPal (custom implementations) ---");
        PaymentProcessorV2 paypal = new PayPalPaymentV2();
        paypal.processPayment(99.99);
        paypal.validatePaymentMethod();  // Custom
        paypal.processRecurring(9.99, "monthly");  // Custom

        System.out.println("\n=== Benefits of Default Methods ===");
        System.out.println("""
            1. Backward Compatibility
               - Add methods without breaking existing code
               - Old implementations continue to work

            2. Optional Features
               - Provide sensible defaults
               - Override only when needed

            3. Interface Evolution
               - Grow APIs over time
               - No need for adapter classes

            4. Utility Methods
               - Add helper methods to interfaces
               - All implementations can use them
            """);

        System.out.println("=== Common Use Cases ===");
        System.out.println("""
            - Collection.forEach() - added in Java 8
            - Comparator.reversed() - added in Java 8
            - List.sort() - added in Java 8
            - Stream API methods on collections
            """);
    }
}
  1. stripe ← ⟨StripePaymentV2 A⟩

    90public class RealWorldEvolution {91    public static void main(String[] args) {92        System.out.println("=== API Evolution Example ===\n");93        94        System.out.println("--- Stripe (uses defaults) ---");95        PaymentProcessorV2 stripe→ ⟨StripePaymentV2 A⟩ = new StripePaymentV2();96        stripe.processPayment(99.99);97        stripe.validatePaymentMethod();  // Uses default //?stripeusedefault
    output=== API Evolution Example ===
    --- Stripe (uses defaults) ---
  2. @Override public boolean processPayment(double amount)

    pass 1 of 2
    47class StripePaymentV2 implements PaymentProcessorV2 {48    @Override49    public boolean processPayment(double amount99.99) {50        System.out.println("Stripe: Processing " + formatAmount(amount99.99)); //?usedefaultutility51        return true;
  3. default String formatAmount(double amount)

    pass 1 of 4
    40// NEW in V2 - utility method //?utility41default String formatAmount(double amount99.99) {42    return String.format("$%.2f", amount99.99);43}
    All 4 passes — pass 1 is the card above
    passamount
    199.99
    29.99
    399.99
    49.99
  4. System.out.println("Stripe: Processing " + formatAmount(amount)); //?u…

    49public boolean processPayment(double amount) {50    System.out.println("Stripe: Processing " + formatAmount(amount99.99)); //?usedefaultutility51    return true;52}
    outputStripe: Processing $99.99
  5. stripe.processPayment(99.99);

    95PaymentProcessorV2 stripe = new StripePaymentV2();96stripe.processPayment(99.99);97stripe.validatePaymentMethod();  // Uses default //?stripeusedefault98stripe.processRecurring(9.99, "monthly");  // Uses default
  6. default void validatePaymentMethod()

    35// NEW in V2 //?anotherneew36default void validatePaymentMethod() {37    System.out.println("Default validation passed");38}
    outputDefault validation passed
  7. stripe.validatePaymentMethod(); // Uses default //?stripeusedefault

    96stripe.processPayment(99.99);97stripe.validatePaymentMethod();  // Uses default //?stripeusedefault98stripe.processRecurring(9.99, "monthly");  // Uses default
  8. default boolean processRecurring(double amount, String schedule)

    29// NEW in V2 - won't break existing implementations! //?newmethod30default boolean processRecurring(double amount9.99, String schedulemonthly) {31    System.out.println("Default recurring: $" + amount9.99 + " " + schedulemonthly);32    return processPayment(amount9.99);  // Fallback to regular payment //?fallback33}
    outputDefault recurring: $9.99 monthly
  9. @Override public boolean processPayment(double amount)

    pass 2 of 2
    47class StripePaymentV2 implements PaymentProcessorV2 {48    @Override49    public boolean processPayment(double amount9.99) {50        System.out.println("Stripe: Processing " + formatAmount(amount9.99)); //?usedefaultutility51        return true;
  10. System.out.println("Stripe: Processing " + formatAmount(amount)); //?u…

    49public boolean processPayment(double amount) {50    System.out.println("Stripe: Processing " + formatAmount(amount9.99)); //?usedefaultutility51    return true;52}
    outputStripe: Processing $9.99
  11. paypal ← ⟨PayPalPaymentV2 B⟩

    97stripe.validatePaymentMethod();  // Uses default //?stripeusedefault98stripe.processRecurring(9.99, "monthly");  // Uses default99100System.out.println("\n--- PayPal (custom implementations) ---");101PaymentProcessorV2 paypal→ ⟨PayPalPaymentV2 B⟩ = new PayPalPaymentV2();102paypal.processPayment(99.99);103paypal.validatePaymentMethod();  // Custom //?paypalcustom
    output
    --- PayPal (custom implementations) ---
  12. @Override public boolean processPayment(double amount)

    64class PayPalPaymentV2 implements PaymentProcessorV2 {65    @Override66    public boolean processPayment(double amount99.99) {67        System.out.println("PayPal: Processing " + formatAmount(amount99.99));68        return true;
  13. System.out.println("PayPal: Processing " + formatAmount(amount));

    66public boolean processPayment(double amount) {67    System.out.println("PayPal: Processing " + formatAmount(amount99.99));68    return true;69}
    outputPayPal: Processing $99.99
  14. paypal.processPayment(99.99);

    101PaymentProcessorV2 paypal = new PayPalPaymentV2();102paypal.processPayment(99.99);103paypal.validatePaymentMethod();  // Custom //?paypalcustom104paypal.processRecurring(9.99, "monthly");  // Custom
  15. @Override public void validatePaymentMethod()

    84@Override85public void validatePaymentMethod() {86    System.out.println("PayPal: Validating linked account...");87}
    outputPayPal: Validating linked account...
  16. paypal.validatePaymentMethod(); // Custom //?paypalcustom

    102paypal.processPayment(99.99);103paypal.validatePaymentMethod();  // Custom //?paypalcustom104paypal.processRecurring(9.99, "monthly");  // Custom
  17. @Override public boolean processRecurring(double amount, String sc…

    77@Override78public boolean processRecurring(double amount9.99, String schedulemonthly) { //?overridenew79    System.out.println("PayPal Subscriptions: " + formatAmount(amount9.99) + 80                      " billed " + schedulemonthly);81    return true;
  18. System.out.println("PayPal Subscriptions: " + formatAmount(amount) +

    78public boolean processRecurring(double amount, String schedule) { //?overridenew79    System.out.println("PayPal Subscriptions: " + formatAmount(amount9.99) + 80                      " billed " + schedulemonthly);81    return true;82}
    outputPayPal Subscriptions: $9.99 billed monthly
  19. paypal.processRecurring(9.99, "monthly"); // Custom

    103    paypal.validatePaymentMethod();  // Custom //?paypalcustom104    paypal.processRecurring(9.99, "monthly");  // Custom105    106    System.out.println("\n=== Benefits of Default Methods ===");107    System.out.println("""108        1. Backward Compatibility109           - Add methods without breaking existing code110           - Old implementations continue to work111        112        2. Optional Features113           - Provide sensible defaults114           - Override only when needed115        116        3. Interface Evolution117           - Grow APIs over time118           - No need for adapter classes119        120        4. Utility Methods121           - Add helper methods to interfaces122           - All implementations can use them123        """);124    125    System.out.println("=== Common Use Cases ===");126    System.out.println("""127        - Collection.forEach() - added in Java 8128        - Comparator.reversed() - added in Java 8129        - List.sort() - added in Java 8130        - Stream API methods on collections131        """);132}
    output
    === Benefits of Default Methods ===
    1. Backward Compatibility
       - Add methods without breaking existing code
       - Old implementations continue to work
    
    2. Optional Features
       - Provide sensible defaults
       - Override only when needed
    
    3. Interface Evolution
       - Grow APIs over time
       - No need for adapter classes
    
    4. Utility Methods
       - Add helper methods to interfaces
       - All implementations can use them
    === Common Use Cases ===
    - Collection.forEach() - added in Java 8
    - Comparator.reversed() - added in Java 8
    - List.sort() - added in Java 8
    - Stream API methods on collections

Collections API added forEach(), stream() via default methods. No breakage.

Exercise: Practical.java

Evolve an interface with new default methods