OOP Advanced
Default Methods
Interface Evolution
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.
// 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
""");
}
}
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 ===this.name ← Alice
21Person(String nameAlice) {22 this.name→ Alice = nameAlice;23}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());@Override public String getName()
pass 1 of 325@Override26public String getName() { //?mustimplement27 return nameAlice;28}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: AliceSystem.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!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 defaultoutputGoodbye, Alice! === Another Implementation ===@Override public String getName()
50Greeting robot = new Greeting() {51 @Override52 public String getName() {53 return robotNameRobot-3000;54 }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
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 ===this.name ← Mina
21Person(String nameMina) {22 this.name→ Mina = nameMina;23}alice ← ⟨Person A⟩
36String personName = "Mina";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());@Override public String getName()
pass 1 of 325@Override26public String getName() {27 return nameMina;28}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: MinaSystem.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!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 defaultoutputGoodbye, Mina! === Another Implementation ===@Override public String getName()
50Greeting robot = new Greeting() {51 @Override52 public String getName() {53 return robotNameRobot-3000;54 }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
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 ===this.name ← Jordan
21Person(String nameJordan) {22 this.name→ Jordan = nameJordan;23}alice ← ⟨Person A⟩
36String personName = "Jordan";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());@Override public String getName()
pass 1 of 325@Override26public String getName() {27 return nameJordan;28}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: JordanSystem.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!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 defaultoutputGoodbye, Jordan! === Another Implementation ===@Override public String getName()
50Greeting robot = new Greeting() {51 @Override52 public String getName() {53 return robotNameRobot-3000;54 }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
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 ===this.name ← Alice
21Person(String nameAlice) {22 this.name→ Alice = nameAlice;23}alice ← ⟨Person A⟩
36String personName = "Alice";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());@Override public String getName()
pass 1 of 325@Override26public String getName() {27 return nameAlice;28}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: AliceSystem.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!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 defaultoutputGoodbye, Alice! === Another Implementation ===@Override public String getName()
50Greeting robot = new Greeting() {51 @Override52 public String getName() {53 return robotNameHelperBot;54 }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
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 ===this.name ← Alice
21Person(String nameAlice) {22 this.name→ Alice = nameAlice;23}alice ← ⟨Person A⟩
36String personName = "Alice";37Person alice→ ⟨Person A⟩ = new Person(personName);3839// Calling abstract method (implemented)40System.out.println("Name: " + alice.getName());@Override public String getName()
pass 1 of 325@Override26public String getName() {27 return nameAlice;28}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: AliceSystem.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!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 defaultoutputGoodbye, Alice! === Another Implementation ===@Override public String getName()
50Greeting robot = new Greeting() {51 @Override52 public String getName() {53 return robotNameGuideBot;54 }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.
Override default methods
Implementing classes can override defaults.
// 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");
}
}
}
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) ===default void log(String message)
pass 1 of 23interface Logger { //?loggerinterface4 default void log(String messageApplication started) { //?defaultlog5 System.out.println("[LOG] " + messageApplication started);6 }output[LOG] Application startedbasic.log("Application started");
66Logger basic = new BasicLogger();67basic.log("Application started");68basic.warn("Low memory");69basic.error("Connection failed");default void warn(String message)
pass 1 of 28default void warn(String messageLow memory) {9 System.out.println("[WARN] " + messageLow memory);10}output[WARN] Low memorybasic.warn("Low memory");
67basic.log("Application started");68basic.warn("Low memory");69basic.error("Connection failed");default void error(String message)
12default void error(String messageConnection failed) {13 System.out.println("[ERROR] " + messageConnection failed);14}output[ERROR] Connection failedtimestamp ← ⟨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! //?partialdefaultoutput === TimestampLogger (partial override) ===@Override public void log(String message)
pass 1 of 223class TimestampLogger implements Logger {24 @Override25 public void log(String messageApplication started) { //?overridelog26 System.out.println("[" + timestamp() + "] LOG: " + messageApplication started);27 }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 starteddefault void warn(String message)
pass 2 of 28default void warn(String messageLow memory) {9 System.out.println("[WARN] " + messageLow memory);10}output[WARN] Low memorytimestamp.warn("Low memory"); // Uses default! //?partialdefault
73timestamp.log("Application started");74timestamp.warn("Low memory"); // Uses default! //?partialdefault75timestamp.error("Connection failed");@Override public void error(String message)
29@Override30public void error(String messageConnection failed) { //?overrideerror31 System.out.println("!!! [" + timestamp() + "] ERROR: " + messageConnection failed + " !!!");32}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) ===@Override public void log(String message)
pass 1 of 242class JsonLogger implements Logger {43 @Override44 public void log(String messageApplication started) {45 System.out.println(format("LOG", messageApplication started));46 }private String format(String level, String msg)
pass 1 of 458private String format(String levelLOG, String msgApplication started) {59 return "{\"level\":\"" + levelLOG + "\",\"message\":\"" + msgApplication started + "\"}";60}All 4 passes — pass 1 is the card above pass levelmsg1 LOG Application started 2 WARN Low memory 3 ERROR Connection failed 4 LOG Test message 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"}@Override public void warn(String message)
48@Override49public void warn(String messageLow memory) {50 System.out.println(format("WARN", messageLow memory));51}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"}@Override public void error(String message)
53@Override54public void error(String messageConnection failed) {55 System.out.println(format("ERROR", messageConnection failed));56}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 ===for (Logger logger : loggers)
pass 1 of 387for (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 pass logger1 ⟨BasicLogger D⟩ 2 ⟨TimestampLogger E⟩ 3 ⟨JsonLogger F⟩ default void log(String message)
pass 2 of 23interface Logger { //?loggerinterface4 default void log(String messageTest message) { //?defaultlog5 System.out.println("[LOG] " + messageTest message);6 }output[LOG] Test messagelogger.log("Test message");
88 System.out.println("\n" + logger.getClass().getSimpleName() + ":");89 logger.log("Test message");90}@Override public void log(String message)
pass 2 of 223class TimestampLogger implements Logger {24 @Override25 public void log(String messageTest message) { //?overridelog26 System.out.println("[" + timestamp() + "] LOG: " + messageTest message);27 }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@Override public void log(String message)
pass 2 of 242class JsonLogger implements Logger {43 @Override44 public void log(String messageTest message) {45 System.out.println(format("LOG", messageTest message));46 }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.
// 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
""");
}
}
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 ===default void move()
pass 1 of 33interface Flyable { //?flyable4 default void move() { //?flymove5 System.out.println("Flying through the air");6 }outputFlying through the airbird.move();
67Bird bird = new Bird();68bird.move();69bird.takeOff();default void takeOff()
pass 1 of 28default void takeOff() {9 System.out.println("Taking off...");10}outputTaking off...fish ← ⟨Fish B⟩
68bird.move();69bird.takeOff();7071Fish fish→ ⟨Fish B⟩ = new Fish();72fish.move();73fish.dive();default void move()
pass 1 of 213interface Swimmable { //?swimmable14 default void move() { //?swimmove15 System.out.println("Swimming through the water");16 }outputSwimming through the waterfish.move();
71Fish fish = new Fish();72fish.move();73fish.dive();default void dive()
pass 1 of 218default void dive() {19 System.out.println("Diving deep...");20}outputDiving deep...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 Flyableoutput === Diamond Problem Resolution ===@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!duck.move(); // Duck's override
76Duck duck = new Duck();77duck.move(); // Duck's override78duck.takeOff(); // From Flyable79duck.dive(); // From Swimmabledefault void takeOff()
pass 2 of 28default void takeOff() {9 System.out.println("Taking off...");10}outputTaking off...duck.takeOff(); // From Flyable
77duck.move(); // Duck's override78duck.takeOff(); // From Flyable79duck.dive(); // From Swimmabledefault void dive()
pass 2 of 218default void dive() {19 System.out.println("Diving deep...");20}outputDiving deep...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 ===Flyable.super.move(); // Calls Flyable's default
43public void flyMove() {44 Flyable.super.move(); // Calls Flyable's default45}duck.flyMove();
81System.out.println("\n=== Calling Specific Interface's Default ===");82duck.flyMove();83duck.swimMove();default void move()
pass 2 of 213interface Swimmable { //?swimmable14 default void move() { //?swimmove15 System.out.println("Swimming through the water");16 }outputSwimming through the waterSwimmable.super.move(); // Calls Swimmable's default
47public void swimMove() {48 Swimmable.super.move(); // Calls Swimmable's default49}plane ← ⟨Seaplane D⟩
82duck.flyMove();83duck.swimMove();8485System.out.println("\n=== Delegation Approach ===");86Seaplane plane→ ⟨Seaplane D⟩ = new Seaplane();87plane.move(); // Delegates to Flyableoutput === Delegation Approach ===Flyable.super.move(); //?delegateflyable
59 // Delegate to Flyable's version60 Flyable.super.move(); //?delegateflyable61}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.
Default calls abstract
Default methods can use abstract methods.
// 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
""");
}
}
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 ===this.filename ← data.txt
28FileProcessor(String filenamedata.txt) {29 this.filename→ data.txt = filenamedata.txt;30}fileProc ← ⟨FileProcessor A⟩
116DataProcessor fileProc→ ⟨FileProcessor A⟩ = new FileProcessor("data.txt");117fileProc.processAll(data);default void processAll(String[] items)
pass 1 of 28// Default methods - use abstract methods //?defaultuseabstract9default void processAll(String[] items) { //?templatemethod10 System.out.println("Processing from: " + getSource());11 for (String item : items) {@Override public String getSource()
32@Override33public String getSource() { //?implementsource34 return "File: " + filenamedata.txt;35}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.txtfor (String item : items)
pass 1 of 610System.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 pass item1 apple 2 banana 3 cherry 4 apple 5 banana 6 cherry @Override public void processItem(String item)
pass 1 of 337@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 pass item1 apple 2 banana 3 cherry default void validate(String item)
pass 1 of 617default void validate(String itemapple) {18 if (item == null || item.isEmpty()) {All 6 passes — pass 1 is the card above pass item1 apple 2 banana 3 cherry 4 apple 5 banana 6 cherry 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: APPLEvalidate(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: BANANAvalidate(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 itemsfileProc.processAll(data);
116DataProcessor fileProc = new FileProcessor("data.txt");117fileProc.processAll(data);118119System.out.println();120121DataProcessor dbProc = new DatabaseProcessor("fruits");122dbProc.processAll(data);this.tableName ← fruits
47DatabaseProcessor(String tableNamefruits) {48 this.tableName→ fruits = tableNamefruits;49}dbProc ← ⟨DatabaseProcessor B⟩
121DataProcessor dbProc→ ⟨DatabaseProcessor B⟩ = new DatabaseProcessor("fruits");122dbProc.processAll(data);default void processAll(String[] items)
pass 2 of 28// Default methods - use abstract methods //?defaultuseabstract9default void processAll(String[] items) { //?templatemethod10 System.out.println("Processing from: " + getSource());11 for (String item : items) {@Override public String getSource()
51@Override52public String getSource() {53 return "Database: " + tableNamefruits;54}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@Override public void processItem(String item)
pass 1 of 356@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 pass item1 apple 2 banana 3 cherry 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')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')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 itemssimple ← ⟨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:@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...logged ← ⟨LoggedTask D⟩
127Workflow simple = new SimpleTask();128simple.execute();129130System.out.println("\nLoggedTask:");131Workflow logged→ ⟨LoggedTask D⟩ = new LoggedTask();132logged.execute();output LoggedTask:@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...@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...@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!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.
// 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
""");
}
}
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 //?stripeusedefaultoutput=== API Evolution Example === --- Stripe (uses defaults) ---@Override public boolean processPayment(double amount)
pass 1 of 247class StripePaymentV2 implements PaymentProcessorV2 {48 @Override49 public boolean processPayment(double amount99.99) {50 System.out.println("Stripe: Processing " + formatAmount(amount99.99)); //?usedefaultutility51 return true;default String formatAmount(double amount)
pass 1 of 440// 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 pass amount1 99.99 2 9.99 3 99.99 4 9.99 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.99stripe.processPayment(99.99);
95PaymentProcessorV2 stripe = new StripePaymentV2();96stripe.processPayment(99.99);97stripe.validatePaymentMethod(); // Uses default //?stripeusedefault98stripe.processRecurring(9.99, "monthly"); // Uses defaultdefault void validatePaymentMethod()
35// NEW in V2 //?anotherneew36default void validatePaymentMethod() {37 System.out.println("Default validation passed");38}outputDefault validation passedstripe.validatePaymentMethod(); // Uses default //?stripeusedefault
96stripe.processPayment(99.99);97stripe.validatePaymentMethod(); // Uses default //?stripeusedefault98stripe.processRecurring(9.99, "monthly"); // Uses defaultdefault 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@Override public boolean processPayment(double amount)
pass 2 of 247class StripePaymentV2 implements PaymentProcessorV2 {48 @Override49 public boolean processPayment(double amount9.99) {50 System.out.println("Stripe: Processing " + formatAmount(amount9.99)); //?usedefaultutility51 return true;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.99paypal ← ⟨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 //?paypalcustomoutput --- PayPal (custom implementations) ---@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;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.99paypal.processPayment(99.99);
101PaymentProcessorV2 paypal = new PayPalPaymentV2();102paypal.processPayment(99.99);103paypal.validatePaymentMethod(); // Custom //?paypalcustom104paypal.processRecurring(9.99, "monthly"); // Custom@Override public void validatePaymentMethod()
84@Override85public void validatePaymentMethod() {86 System.out.println("PayPal: Validating linked account...");87}outputPayPal: Validating linked account...paypal.validatePaymentMethod(); // Custom //?paypalcustom
102paypal.processPayment(99.99);103paypal.validatePaymentMethod(); // Custom //?paypalcustom104paypal.processRecurring(9.99, "monthly"); // Custom@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;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 monthlypaypal.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