Introduction
Imagine you're managing a library system, and you need to add new features to all books without breaking existing code. Default methods in Java interfaces are like adding new chapters to a book contract - all books automatically get the new content, but can choose to rewrite it if they want!
Default methods were introduced in Java 8 to allow interfaces to have method implementations without breaking existing implementations. This revolutionized interface design and enabled powerful new patterns.
What are Default Methods?
Default methods are interface methods that have an implementation. They're declared with the default keyword and provide a default implementation that implementing classes can use or override.
Key Characteristics:
- β Method with implementation in interface
- β
Uses
defaultkeyword - β Backward compatible - doesn't break existing implementations
- β Can be overridden by implementing classes
- β Enables interface evolution
Why Default Methods Were Introduced?
Before Java 8:
interface List {
void add(Object item); // All implementing classes MUST implement this
// Cannot add new methods without breaking all implementations
}
After Java 8:
interface List {
void add(Object item);
default void sort() { // New method with default implementation
// sorting logic here
}
}
Code Explanation with Examples
Example 1: Basic Default Methods
// π― INTERFACE WITH DEFAULT METHODS
interface Vehicle {
// π― REGULAR ABSTRACT METHOD (must be implemented)
void start();
void stop();
// π― DEFAULT METHOD (has implementation)
default void honk() {
System.out.println("π Vehicle is honking: Beep Beep!");
}
// π― ANOTHER DEFAULT METHOD
default void displayInfo() {
System.out.println("This is a vehicle interface");
}
// π― STATIC METHOD IN INTERFACE (Java 8+)
static void vehicleFactoryInfo() {
System.out.println("π Vehicle Factory - All vehicles must implement start() and stop()");
}
}
// π― CLASS IMPLEMENTING INTERFACE (CAN USE DEFAULT METHODS AS-IS)
class Car implements Vehicle {
private String model;
public Car(String model) {
this.model = model;
}
// π― MUST IMPLEMENT ABSTRACT METHODS
@Override
public void start() {
System.out.println("π " + model + " car is starting... Vroom!");
}
@Override
public void stop() {
System.out.println("π " + model + " car is stopping...");
}
// π― CAN USE DEFAULT METHODS WITHOUT IMPLEMENTING THEM
// honk() and displayInfo() are inherited automatically
}
// π― CLASS THAT OVERRIDES DEFAULT METHOD
class Truck implements Vehicle {
private String model;
public Truck(String model) {
this.model = model;
}
@Override
public void start() {
System.out.println("π " + model + " truck is starting... BRRRRUM!");
}
@Override
public void stop() {
System.out.println("π " + model + " truck is stopping...");
}
// π― OVERRIDING DEFAULT METHOD
@Override
public void honk() {
System.out.println("π " + model + " truck is honking: HOOONK! HOOONK!");
}
// π― ADDING NEW METHOD
public void loadCargo() {
System.out.println("π " + model + " is loading cargo...");
}
}
// π― CLASS THAT USES DEFAULT METHOD AND ADDS ITS OWN BEHAVIOR
class Motorcycle implements Vehicle {
private String model;
public Motorcycle(String model) {
this.model = model;
}
@Override
public void start() {
System.out.println("ποΈ " + model + " motorcycle is starting... Vroom vroom!");
}
@Override
public void stop() {
System.out.println("ποΈ " + model + " motorcycle is stopping...");
}
// π― USING DEFAULT METHOD BUT EXTENDING IT
@Override
public void displayInfo() {
Vehicle.super.displayInfo(); // Call default implementation
System.out.println("ποΈ Specifically, this is a " + model + " motorcycle");
}
}
public class BasicDefaultMethods {
public static void main(String[] args) {
System.out.println("=== BASIC DEFAULT METHODS DEMONSTRATION ===");
// π― CREATING VEHICLES
Car car = new Car("Toyota Camry");
Truck truck = new Truck("Ford F-150");
Motorcycle motorcycle = new Motorcycle("Harley Davidson");
// π― USING ABSTRACT METHODS (MUST BE IMPLEMENTED)
System.out.println("\n1. ABSTRACT METHODS (MUST IMPLEMENT):");
car.start();
truck.start();
motorcycle.start();
// π― USING DEFAULT METHODS (INHERITED AUTOMATICALLY)
System.out.println("\n2. DEFAULT METHODS (AUTOMATICALLY AVAILABLE):");
System.out.println("--- Car (uses default honk) ---");
car.honk();
car.displayInfo();
System.out.println("--- Truck (overrides default honk) ---");
truck.honk();
truck.displayInfo();
System.out.println("--- Motorcycle (uses default honk, extends displayInfo) ---");
motorcycle.honk();
motorcycle.displayInfo();
// π― STATIC METHOD IN INTERFACE
System.out.println("\n3. STATIC METHOD IN INTERFACE:");
Vehicle.vehicleFactoryInfo();
// π― POLYMORPHISM WITH DEFAULT METHODS
System.out.println("\n4. POLYMORPHISM WITH DEFAULT METHODS:");
Vehicle[] vehicles = {car, truck, motorcycle};
for (Vehicle vehicle : vehicles) {
System.out.println("\n--- Processing Vehicle ---");
vehicle.start();
vehicle.honk(); // Calls appropriate version (default or overridden)
vehicle.displayInfo();
vehicle.stop();
}
// π― BENEFITS OF DEFAULT METHODS
System.out.println("\n5. BENEFITS OF DEFAULT METHODS:");
System.out.println("β
Backward Compatibility: Add methods without breaking existing code");
System.out.println("β
Code Reuse: Common implementation in one place");
System.out.println("β
Flexibility: Classes can use default or provide their own");
System.out.println("β
Interface Evolution: Interfaces can grow over time");
// π― TRUCK-SPECIFIC METHOD
System.out.println("\n6. CLASS-SPECIFIC METHODS:");
truck.loadCargo();
}
}
Output:
=== BASIC DEFAULT METHODS DEMONSTRATION === 1. ABSTRACT METHODS (MUST IMPLEMENT): π Toyota Camry car is starting... Vroom! π Ford F-150 truck is starting... BRRRRUM! ποΈ Harley Davidson motorcycle is starting... Vroom vroom! 2. DEFAULT METHODS (AUTOMATICALLY AVAILABLE): --- Car (uses default honk) --- π Vehicle is honking: Beep Beep! This is a vehicle interface --- Truck (overrides default honk) --- π Ford F-150 truck is honking: HOOONK! HOOONK! This is a vehicle interface --- Motorcycle (uses default honk, extends displayInfo) --- π Vehicle is honking: Beep Beep! This is a vehicle interface ποΈ Specifically, this is a Harley Davidson motorcycle 3. STATIC METHOD IN INTERFACE: π Vehicle Factory - All vehicles must implement start() and stop() 4. POLYMORPHISM WITH DEFAULT METHODS: --- Processing Vehicle --- π Toyota Camry car is starting... Vroom! π Vehicle is honking: Beep Beep! This is a vehicle interface π Toyota Camry car is stopping... --- Processing Vehicle --- π Ford F-150 truck is starting... BRRRRUM! π Ford F-150 truck is honking: HOOONK! HOOONK! This is a vehicle interface π Ford F-150 truck is stopping... --- Processing Vehicle --- ποΈ Harley Davidson motorcycle is starting... Vroom vroom! π Vehicle is honking: Beep Beep! This is a vehicle interface ποΈ Specifically, this is a Harley Davidson motorcycle ποΈ Harley Davidson motorcycle is stopping... 5. BENEFITS OF DEFAULT METHODS: β Backward Compatibility: Add methods without breaking existing code β Code Reuse: Common implementation in one place β Flexibility: Classes can use default or provide their own β Interface Evolution: Interfaces can grow over time 6. CLASS-SPECIFIC METHODS: π Ford F-150 is loading cargo...
Example 2: Real-World Library System
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
// π― LIBRARY ITEM INTERFACE WITH DEFAULT METHODS
interface LibraryItem {
// π― ABSTRACT METHODS
String getTitle();
String getAuthor();
String getItemId();
boolean isAvailable();
// π― DEFAULT METHODS - COMMON FUNCTIONALITY
default void checkOut(String borrower) {
System.out.println("π " + getTitle() + " checked out to: " + borrower);
System.out.println(" Due date: " + calculateDueDate());
}
default void checkIn() {
System.out.println("π " + getTitle() + " has been returned");
}
default LocalDate calculateDueDate() {
return LocalDate.now().plusWeeks(2); // 2 weeks loan period
}
default void displayBasicInfo() {
System.out.println("π " + getTitle() + " by " + getAuthor());
System.out.println(" ID: " + getItemId() + " | Available: " + isAvailable());
}
// π― STATIC UTILITY METHOD
static void displayLibraryRules() {
System.out.println("=== LIBRARY RULES ===");
System.out.println("1. Loan period: 2 weeks");
System.out.println("2. Maximum 10 items per borrower");
System.out.println("3. Late fee: $0.25 per day");
}
}
// π― BOOK CLASS IMPLEMENTING INTERFACE
class Book implements LibraryItem {
private String title;
private String author;
private String isbn;
private boolean available;
private int pageCount;
public Book(String title, String author, String isbn, int pageCount) {
this.title = title;
this.author = author;
this.isbn = isbn;
this.pageCount = pageCount;
this.available = true;
}
// π― IMPLEMENT ABSTRACT METHODS
@Override
public String getTitle() { return title; }
@Override
public String getAuthor() { return author; }
@Override
public String getItemId() { return isbn; }
@Override
public boolean isAvailable() { return available; }
// π― USING DEFAULT METHODS AS-IS
// checkOut(), checkIn(), calculateDueDate(), displayBasicInfo() inherited
// π― BOOK-SPECIFIC METHOD
public void readSample() {
System.out.println("π Reading sample of: " + title);
System.out.println(" Pages: " + pageCount);
}
}
// π― DVD CLASS IMPLEMENTING INTERFACE
class DVD implements LibraryItem {
private String title;
private String director;
private String dvdId;
private boolean available;
private int durationMinutes;
public DVD(String title, String director, String dvdId, int durationMinutes) {
this.title = title;
this.director = director;
this.dvdId = dvdId;
this.durationMinutes = durationMinutes;
this.available = true;
}
// π― IMPLEMENT ABSTRACT METHODS
@Override
public String getTitle() { return title; }
@Override
public String getAuthor() { return director; } // Director as "author"
@Override
public String getItemId() { return dvdId; }
@Override
public boolean isAvailable() { return available; }
// π― OVERRIDE DEFAULT METHOD FOR DVD-SPECIFIC BEHAVIOR
@Override
public void checkOut(String borrower) {
System.out.println("π " + title + " DVD checked out to: " + borrower);
System.out.println(" Due date: " + calculateDueDate());
System.out.println(" Duration: " + durationMinutes + " minutes");
}
@Override
public LocalDate calculateDueDate() {
return LocalDate.now().plusWeeks(1); // DVDs have 1-week loan
}
// π― DVD-SPECIFIC METHOD
public void playTrailer() {
System.out.println("π¬ Playing trailer for: " + title);
}
}
// π― AUDIOBOOK CLASS IMPLEMENTING INTERFACE
class Audiobook implements LibraryItem {
private String title;
private String author;
private String audioId;
private boolean available;
private int audioLength; // in minutes
private String narrator;
public Audiobook(String title, String author, String audioId, int audioLength, String narrator) {
this.title = title;
this.author = author;
this.audioId = audioId;
this.audioLength = audioLength;
this.narrator = narrator;
this.available = true;
}
// π― IMPLEMENT ABSTRACT METHODS
@Override
public String getTitle() { return title; }
@Override
public String getAuthor() { return author; }
@Override
public String getItemId() { return audioId; }
@Override
public boolean isAvailable() { return available; }
// π― OVERRIDE DEFAULT METHOD
@Override
public void displayBasicInfo() {
System.out.println("π§ " + title + " by " + author);
System.out.println(" Narrated by: " + narrator);
System.out.println(" Duration: " + audioLength + " minutes");
System.out.println(" ID: " + audioId + " | Available: " + available);
}
// π― AUDIOBOOK-SPECIFIC METHOD
public void playSample() {
System.out.println("π§ Playing sample of: " + title);
System.out.println(" Narrated by: " + narrator);
}
}
// π― LIBRARY MANAGEMENT SYSTEM
class Library {
private List<LibraryItem> items;
private String libraryName;
public Library(String libraryName) {
this.libraryName = libraryName;
this.items = new ArrayList<>();
System.out.println("ποΈ Library created: " + libraryName);
}
public void addItem(LibraryItem item) {
items.add(item);
System.out.println("β Added to library: " + item.getTitle());
}
public void displayAllItems() {
System.out.println("\n=== " + libraryName.toUpperCase() + " CATALOG ===");
for (LibraryItem item : items) {
item.displayBasicInfo(); // Using default method
System.out.println("---");
}
}
public void checkoutItem(String itemId, String borrower) {
for (LibraryItem item : items) {
if (item.getItemId().equals(itemId) && item.isAvailable()) {
item.checkOut(borrower); // Using default method (may be overridden)
return;
}
}
System.out.println("β Item not available or not found: " + itemId);
}
// π― SEARCH FUNCTIONALITY USING DEFAULT METHODS
public void searchByAuthor(String author) {
System.out.println("\nπ Search results for author: " + author);
boolean found = false;
for (LibraryItem item : items) {
if (item.getAuthor().toLowerCase().contains(author.toLowerCase())) {
item.displayBasicInfo();
found = true;
}
}
if (!found) {
System.out.println("No items found for author: " + author);
}
}
}
public class LibrarySystemDemo {
public static void main(String[] args) {
System.out.println("=== REAL-WORLD LIBRARY SYSTEM WITH DEFAULT METHODS ===");
// π― DISPLAY LIBRARY RULES (STATIC METHOD)
LibraryItem.displayLibraryRules();
// π― CREATE LIBRARY
Library cityLibrary = new Library("City Public Library");
// π― ADD VARIOUS ITEMS TO LIBRARY
System.out.println("\n1. ADDING ITEMS TO LIBRARY:");
Book book1 = new Book("The Great Gatsby", "F. Scott Fitzgerald", "ISBN-001", 218);
Book book2 = new Book("To Kill a Mockingbird", "Harper Lee", "ISBN-002", 324);
DVD dvd1 = new DVD("Inception", "Christopher Nolan", "DVD-001", 148);
DVD dvd2 = new DVD("The Shawshank Redemption", "Frank Darabont", "DVD-002", 142);
Audiobook audio1 = new Audiobook("The Hobbit", "J.R.R. Tolkien", "AUDIO-001", 682, "Rob Inglis");
cityLibrary.addItem(book1);
cityLibrary.addItem(book2);
cityLibrary.addItem(dvd1);
cityLibrary.addItem(dvd2);
cityLibrary.addItem(audio1);
// π― DISPLAY ALL ITEMS
cityLibrary.displayAllItems();
// π― CHECKOUT OPERATIONS
System.out.println("\n2. CHECKOUT OPERATIONS:");
cityLibrary.checkoutItem("ISBN-001", "Alice Johnson");
cityLibrary.checkoutItem("DVD-001", "Bob Smith");
cityLibrary.checkoutItem("AUDIO-001", "Carol Davis");
// π― SEARCH FUNCTIONALITY
System.out.println("\n3. SEARCH FUNCTIONALITY:");
cityLibrary.searchByAuthor("Tolkien");
cityLibrary.searchByAuthor("Nolan");
// π― ITEM-SPECIFIC OPERATIONS
System.out.println("\n4. ITEM-SPECIFIC OPERATIONS:");
book1.readSample();
dvd1.playTrailer();
audio1.playSample();
// π― POLYMORPHISM WITH DEFAULT METHODS
System.out.println("\n5. POLYMORPHIC PROCESSING:");
LibraryItem[] items = {book1, book2, dvd1, dvd2, audio1};
for (LibraryItem item : items) {
System.out.println("\n--- Processing " + item.getClass().getSimpleName() + " ---");
item.displayBasicInfo(); // Each calls appropriate version
if (item instanceof Book) {
((Book) item).readSample();
} else if (item instanceof DVD) {
((DVD) item).playTrailer();
} else if (item instanceof Audiobook) {
((Audiobook) item).playSample();
}
}
// π― BENEFITS IN REAL-WORLD SYSTEMS
System.out.println("\n6. REAL-WORLD BENEFITS:");
System.out.println("β
Common Operations: checkout/checkin logic in one place");
System.out.println("β
Flexible Implementation: Each item type can customize behavior");
System.out.println("β
Backward Compatible: Can add new item types without changes");
System.out.println("β
Consistent API: All library items have same basic operations");
System.out.println("β
Reduced Code Duplication: Common logic in interface");
}
}
Output:
=== REAL-WORLD LIBRARY SYSTEM WITH DEFAULT METHODS === === LIBRARY RULES === 1. Loan period: 2 weeks 2. Maximum 10 items per borrower 3. Late fee: $0.25 per day ποΈ Library created: City Public Library 1. ADDING ITEMS TO LIBRARY: β Added to library: The Great Gatsby β Added to library: To Kill a Mockingbird β Added to library: Inception β Added to library: The Shawshank Redemption β Added to library: The Hobbit === CITY PUBLIC LIBRARY CATALOG === π The Great Gatsby by F. Scott Fitzgerald ID: ISBN-001 | Available: true --- π To Kill a Mockingbird by Harper Lee ID: ISBN-002 | Available: true --- π Inception by Christopher Nolan ID: DVD-001 | Available: true --- π The Shawshank Redemption by Frank Darabont ID: DVD-002 | Available: true --- π§ The Hobbit by J.R.R. Tolkien Narrated by: Rob Inglis Duration: 682 minutes ID: AUDIO-001 | Available: true --- 2. CHECKOUT OPERATIONS: π The Great Gatsby checked out to: Alice Johnson Due date: 2024-01-14 π Inception DVD checked out to: Bob Smith Due date: 2024-01-07 Duration: 148 minutes π The Hobbit checked out to: Carol Davis Due date: 2024-01-14 3. SEARCH FUNCTIONALITY: π Search results for author: Tolkien π§ The Hobbit by J.R.R. Tolkien Narrated by: Rob Inglis Duration: 682 minutes ID: AUDIO-001 | Available: false π Search results for author: Nolan π Inception by Christopher Nolan ID: DVD-001 | Available: false 4. ITEM-SPECIFIC OPERATIONS: π Reading sample of: The Great Gatsby Pages: 218 π¬ Playing trailer for: Inception π§ Playing sample of: The Hobbit Narrated by: Rob Inglis 5. POLYMORPHIC PROCESSING: --- Processing Book --- π The Great Gatsby by F. Scott Fitzgerald ID: ISBN-001 | Available: false π Reading sample of: The Great Gatsby Pages: 218 --- Processing Book --- π To Kill a Mockingbird by Harper Lee ID: ISBN-002 | Available: true π Reading sample of: To Kill a Mockingbird Pages: 324 --- Processing DVD --- π Inception by Christopher Nolan ID: DVD-001 | Available: false π¬ Playing trailer for: Inception --- Processing DVD --- π The Shawshank Redemption by Frank Darabont ID: DVD-002 | Available: true π¬ Playing trailer for: The Shawshank Redemption --- Processing Audiobook --- π§ The Hobbit by J.R.R. Tolkien Narrated by: Rob Inglis Duration: 682 minutes ID: AUDIO-001 | Available: false π§ Playing sample of: The Hobbit Narrated by: Rob Inglis 6. REAL-WORLD BENEFITS: β Common Operations: checkout/checkin logic in one place β Flexible Implementation: Each item type can customize behavior β Backward Compatible: Can add new item types without changes β Consistent API: All library items have same basic operations β Reduced Code Duplication: Common logic in interface
Example 3: Multiple Inheritance and Diamond Problem
// π― INTERFACE 1: FLYABLE
interface Flyable {
default void fly() {
System.out.println("βοΈ Flying through the air...");
}
default void takeOff() {
System.out.println("π« Taking off from ground...");
}
default void land() {
System.out.println("π¬ Landing safely...");
}
// π― ABSTRACT METHOD
int getMaxAltitude();
}
// π― INTERFACE 2: SWIMMABLE
interface Swimmable {
default void swim() {
System.out.println("π Swimming in water...");
}
default void dive() {
System.out.println("π€Ώ Diving underwater...");
}
default void surface() {
System.out.println("π Surfacing from water...");
}
// π― ABSTRACT METHOD
int getMaxDepth();
}
// π― INTERFACE 3: WALKABLE
interface Walkable {
default void walk() {
System.out.println("πΆ Walking on ground...");
}
default void run() {
System.out.println("π Running fast...");
}
// π― ABSTRACT METHOD
int getMaxSpeed();
}
// π― INTERFACE EXTENDING OTHERS (MULTIPLE INHERITANCE)
interface Amphibious extends Swimmable, Walkable {
// π― CONFLICT RESOLUTION: Both Swimmable and Walkable have move(), need to resolve
default void move() {
System.out.println("πΈ Amphibious creature moving... can walk or swim!");
}
// π― OVERRIDE DEFAULT METHOD FROM PARENT INTERFACE
@Override
default void swim() {
System.out.println("πΈ Amphibious swimming with webbed feet...");
}
// π― NEW DEFAULT METHOD
default void adaptToEnvironment() {
System.out.println("πΈ Adapting to current environment...");
}
}
// π― CLASS IMPLEMENTING MULTIPLE INTERFACES
class Duck implements Flyable, Swimmable, Walkable {
private String name;
public Duck(String name) {
this.name = name;
System.out.println("π¦ Duck created: " + name);
}
// π― MUST IMPLEMENT ABSTRACT METHODS FROM ALL INTERFACES
@Override
public int getMaxAltitude() {
return 1000; // meters
}
@Override
public int getMaxDepth() {
return 5; // meters
}
@Override
public int getMaxSpeed() {
return 10; // km/h
}
// π― OVERRIDE DEFAULT METHOD (OPTIONAL)
@Override
public void swim() {
System.out.println("π¦ " + name + " is paddling in water...");
}
// π― DUCK-SPECIFIC METHOD
public void quack() {
System.out.println("π¦ " + name + " says: Quack! Quack!");
}
}
// π― CLASS IMPLEMENTING AMPHIBIOUS INTERFACE
class Frog implements Amphibious {
private String name;
public Frog(String name) {
this.name = name;
System.out.println("πΈ Frog created: " + name);
}
// π― MUST IMPLEMENT ABSTRACT METHODS
@Override
public int getMaxDepth() {
return 3; // meters
}
@Override
public int getMaxSpeed() {
return 8; // km/h
}
// π― FROG-SPECIFIC METHOD
public void jump() {
System.out.println("πΈ " + name + " is jumping high!");
}
// π― OVERRIDE DEFAULT METHOD
@Override
public void adaptToEnvironment() {
System.out.println("πΈ " + name + " changing color to match environment...");
}
}
// π― CLASS WITH CONFLICT RESOLUTION
class FlyingFish implements Flyable, Swimmable {
private String name;
public FlyingFish(String name) {
this.name = name;
System.out.println("π Flying Fish created: " + name);
}
// π― MUST IMPLEMENT ABSTRACT METHODS
@Override
public int getMaxAltitude() {
return 50; // meters (can glide above water)
}
@Override
public int getMaxDepth() {
return 100; // meters
}
// π― RESOLVING CONFLICT: Both interfaces might have conflicting methods
// In this case, no conflict because methods have different names
// π― OVERRIDE FLY METHOD FOR FISH-SPECIFIC BEHAVIOR
@Override
public void fly() {
System.out.println("π " + name + " is gliding above water surface...");
}
// π― FISH-SPECIFIC METHOD
public void school() {
System.out.println("π " + name + " is swimming in a school with other fish");
}
}
// π― DEMONSTRATING DIAMOND PROBLEM RESOLUTION
interface A {
default void doSomething() {
System.out.println("A: Doing something...");
}
}
interface B extends A {
@Override
default void doSomething() {
System.out.println("B: Overriding A's method...");
}
}
interface C extends A {
@Override
default void doSomething() {
System.out.println("C: Overriding A's method differently...");
}
}
// π― CLASS FACING DIAMOND PROBLEM - MUST RESOLVE EXPLICITLY
class D implements B, C {
// β COMPILATION ERROR: class D inherits unrelated defaults for doSomething() from B and C
// π― MUST OVERRIDE TO RESOLVE CONFLICT
@Override
public void doSomething() {
// Choose which implementation to use, or provide new one
System.out.println("D: Resolving conflict by providing new implementation");
B.super.doSomething(); // Can call B's version explicitly
}
}
public class MultipleInheritanceDemo {
public static void main(String[] args) {
System.out.println("=== MULTIPLE INHERITANCE AND DIAMOND PROBLEM ===");
// π― CREATING CREATURES WITH MULTIPLE CAPABILITIES
Duck duck = new Duck("Daffy");
Frog frog = new Frog("Kermit");
FlyingFish flyingFish = new FlyingFish("Skippy");
// π― DEMONSTRATING MULTIPLE BEHAVIORS
System.out.println("\n1. DUCK CAPABILITIES:");
System.out.println("--- Flying ---");
duck.takeOff();
duck.fly();
duck.land();
System.out.println("Max altitude: " + duck.getMaxAltitude() + "m");
System.out.println("--- Swimming ---");
duck.swim(); // Overridden version
duck.dive();
System.out.println("Max depth: " + duck.getMaxDepth() + "m");
System.out.println("--- Walking ---");
duck.walk();
duck.run();
System.out.println("Max speed: " + duck.getMaxSpeed() + "km/h");
duck.quack();
// π― FROG WITH AMPHIBIOUS CAPABILITIES
System.out.println("\n2. FROG CAPABILITIES:");
frog.walk();
frog.swim(); // From Amphibious interface (overridden)
frog.move(); // From Amphibious interface
frog.adaptToEnvironment(); // Overridden version
frog.jump();
// π― FLYING FISH CAPABILITIES
System.out.println("\n3. FLYING FISH CAPABILITIES:");
flyingFish.fly(); // Overridden version
flyingFish.swim();
flyingFish.dive();
flyingFish.school();
// π― POLYMORPHISM WITH MULTIPLE INTERFACES
System.out.println("\n4. POLYMORPHIC PROCESSING:");
Flyable[] flyers = {duck, flyingFish};
Swimmable[] swimmers = {duck, frog, flyingFish};
Walkable[] walkers = {duck, frog};
System.out.println("--- Flyers ---");
for (Flyable flyer : flyers) {
flyer.fly();
System.out.println(" Max altitude: " + flyer.getMaxAltitude() + "m");
}
System.out.println("--- Swimmers ---");
for (Swimmable swimmer : swimmers) {
swimmer.swim();
System.out.println(" Max depth: " + swimmer.getMaxDepth() + "m");
}
System.out.println("--- Walkers ---");
for (Walkable walker : walkers) {
walker.walk();
System.out.println(" Max speed: " + walker.getMaxSpeed() + "km/h");
}
// π― DIAMOND PROBLEM RESOLUTION
System.out.println("\n5. DIAMOND PROBLEM RESOLUTION:");
D d = new D();
d.doSomething();
// π― BENEFITS AND CHALLENGES
System.out.println("\n6. MULTIPLE INHERITANCE BENEFITS:");
System.out.println("β
Code Reuse: Multiple behaviors from different interfaces");
System.out.println("β
Flexibility: Mix and match capabilities");
System.out.println("β
No Diamond Problem: Compile-time resolution required");
System.out.println("β
Clear Contracts: Each interface defines specific behavior");
System.out.println("\n7. RESOLUTION RULES:");
System.out.println("Rule 1: Class wins over interface");
System.out.println("Rule 2: Most specific interface wins");
System.out.println("Rule 3: Explicit override required for conflicts");
}
}
Output:
=== MULTIPLE INHERITANCE AND DIAMOND PROBLEM === π¦ Duck created: Daffy πΈ Frog created: Kermit π Flying Fish created: Skippy 1. DUCK CAPABILITIES: --- Flying --- π« Taking off from ground... βοΈ Flying through the air... π¬ Landing safely... Max altitude: 1000m --- Swimming --- π¦ Daffy is paddling in water... π€Ώ Diving underwater... Max depth: 5m --- Walking --- πΆ Walking on ground... π Running fast... Max speed: 10km/h π¦ Daffy says: Quack! Quack! 2. FROG CAPABILITIES: πΆ Walking on ground... πΈ Amphibious swimming with webbed feet... πΈ Amphibious creature moving... can walk or swim! πΈ Kermit changing color to match environment... πΈ Kermit is jumping high! 3. FLYING FISH CAPABILITIES: π Skippy is gliding above water surface... π Swimming in water... π€Ώ Diving underwater... π Skippy is swimming in a school with other fish 4. POLYMORPHIC PROCESSING: --- Flyers --- βοΈ Flying through the air... Max altitude: 1000m π Skippy is gliding above water surface... Max altitude: 50m --- Swimmers --- π¦ Daffy is paddling in water... Max depth: 5m πΈ Amphibious swimming with webbed feet... Max depth: 3m π Swimming in water... Max depth: 100m --- Walkers --- πΆ Walking on ground... Max speed: 10km/h πΆ Walking on ground... Max speed: 8km/h 5. DIAMOND PROBLEM RESOLUTION: D: Resolving conflict by providing new implementation B: Overriding A's method... 6. MULTIPLE INHERITANCE BENEFITS: β Code Reuse: Multiple behaviors from different interfaces β Flexibility: Mix and match capabilities β No Diamond Problem: Compile-time resolution required β Clear Contracts: Each interface defines specific behavior 7. RESOLUTION RULES: Rule 1: Class wins over interface Rule 2: Most specific interface wins Rule 3: Explicit override required for conflicts
Example 4: Advanced Patterns and Best Practices
import java.util.function.Predicate;
import java.util.ArrayList;
import java.util.List;
// π― REPOSITORY PATTERN WITH DEFAULT METHODS
interface Repository<T> {
// π― CRUD OPERATIONS (ABSTRACT)
void save(T entity);
void delete(T entity);
T findById(String id);
List<T> findAll();
// π― DEFAULT METHODS FOR COMMON OPERATIONS
default boolean exists(String id) {
return findById(id) != null;
}
default long count() {
return findAll().size();
}
default void saveAll(List<T> entities) {
for (T entity : entities) {
save(entity);
}
System.out.println("πΎ Saved " + entities.size() + " entities");
}
default void deleteAll() {
List<T> allEntities = findAll();
for (T entity : allEntities) {
delete(entity);
}
System.out.println("ποΈ Deleted " + allEntities.size() + " entities");
}
default List<T> filter(Predicate<T> predicate) {
List<T> result = new ArrayList<>();
for (T entity : findAll()) {
if (predicate.test(entity)) {
result.add(entity);
}
}
return result;
}
// π― STATIC METHOD FOR VALIDATION
static <T> boolean isValidId(String id) {
return id != null && !id.trim().isEmpty() && id.length() >= 1;
}
// π― DEFAULT METHOD WITH FUNCTIONAL INTERFACE
default void processAll(java.util.function.Consumer<T> processor) {
for (T entity : findAll()) {
processor.accept(entity);
}
}
}
// π― USER ENTITY
class User {
private String id;
private String name;
private String email;
private boolean active;
public User(String id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
this.active = true;
}
// π― GETTERS AND SETTERS
public String getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
public boolean isActive() { return active; }
public void setActive(boolean active) { this.active = active; }
@Override
public String toString() {
return "User{id='" + id + "', name='" + name + "', email='" + email + "', active=" + active + "}";
}
}
// π― USER REPOSITORY IMPLEMENTATION
class UserRepository implements Repository<User> {
private List<User> users = new ArrayList<>();
private static int nextId = 1;
@Override
public void save(User user) {
if (user.getId() == null) {
user = new User("USER-" + (nextId++), user.getName(), user.getEmail());
}
// Remove existing user with same ID
users.removeIf(u -> u.getId().equals(user.getId()));
users.add(user);
System.out.println("β
Saved user: " + user.getName());
}
@Override
public void delete(User user) {
if (users.remove(user)) {
System.out.println("β Deleted user: " + user.getName());
}
}
@Override
public User findById(String id) {
return users.stream()
.filter(user -> user.getId().equals(id))
.findFirst()
.orElse(null);
}
@Override
public List<User> findAll() {
return new ArrayList<>(users);
}
// π― OVERRIDE DEFAULT METHOD FOR OPTIMIZATION
@Override
public boolean exists(String id) {
return users.stream().anyMatch(user -> user.getId().equals(id));
}
// π― USER-SPECIFIC METHODS
public List<User> findActiveUsers() {
return filter(User::isActive);
}
public List<User> findByName(String name) {
return filter(user -> user.getName().toLowerCase().contains(name.toLowerCase()));
}
public void deactivateAll() {
processAll(user -> user.setActive(false));
System.out.println("π All users deactivated");
}
}
// π― PRODUCT ENTITY
class Product {
private String id;
private String name;
private double price;
private int stock;
public Product(String id, String name, double price, int stock) {
this.id = id;
this.name = name;
this.price = price;
this.stock = stock;
}
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
public int getStock() { return stock; }
public void setStock(int stock) { this.stock = stock; }
@Override
public String toString() {
return String.format("Product{id='%s', name='%s', price=$%.2f, stock=%d}",
id, name, price, stock);
}
}
// π― PRODUCT REPOSITORY
class ProductRepository implements Repository<Product> {
private List<Product> products = new ArrayList<>();
@Override
public void save(Product product) {
products.removeIf(p -> p.getId().equals(product.getId()));
products.add(product);
System.out.println("β
Saved product: " + product.getName());
}
@Override
public void delete(Product product) {
if (products.remove(product)) {
System.out.println("β Deleted product: " + product.getName());
}
}
@Override
public Product findById(String id) {
return products.stream()
.filter(product -> product.getId().equals(id))
.findFirst()
.orElse(null);
}
@Override
public List<Product> findAll() {
return new ArrayList<>(products);
}
// π― PRODUCT-SPECIFIC METHODS USING DEFAULT METHODS
public List<Product> findOutOfStock() {
return filter(product -> product.getStock() == 0);
}
public List<Product> findByPriceRange(double min, double max) {
return filter(product -> product.getPrice() >= min && product.getPrice() <= max);
}
public double calculateTotalInventoryValue() {
final double[] total = {0};
processAll(product -> total[0] += product.getPrice() * product.getStock());
return total[0];
}
}
public class AdvancedPatternsDemo {
public static void main(String[] args) {
System.out.println("=== ADVANCED PATTERNS WITH DEFAULT METHODS ===");
// π― USER REPOSITORY DEMONSTRATION
System.out.println("\n1. USER REPOSITORY:");
UserRepository userRepo = new UserRepository();
// Create users
User user1 = new User(null, "Alice Johnson", "[email protected]");
User user2 = new User(null, "Bob Smith", "[email protected]");
User user3 = new User(null, "Carol Davis", "[email protected]");
// Save users using default method
userRepo.saveAll(List.of(user1, user2, user3));
// Demonstrate default methods
System.out.println("\n--- Default Method Demonstrations ---");
System.out.println("Total users: " + userRepo.count());
System.out.println("User USER-1 exists: " + userRepo.exists("USER-1"));
// Find active users (using overridden default method)
System.out.println("\nActive users:");
userRepo.findActiveUsers().forEach(System.out::println);
// Deactivate all users
userRepo.deactivateAll();
System.out.println("Active users after deactivation: " + userRepo.findActiveUsers().size());
// π― PRODUCT REPOSITORY DEMONSTRATION
System.out.println("\n2. PRODUCT REPOSITORY:");
ProductRepository productRepo = new ProductRepository();
// Create products
Product p1 = new Product("P1", "Laptop", 999.99, 10);
Product p2 = new Product("P2", "Mouse", 29.99, 0);
Product p3 = new Product("P3", "Keyboard", 79.99, 5);
Product p4 = new Product("P4", "Monitor", 299.99, 3);
productRepo.saveAll(List.of(p1, p2, p3, p4));
// Demonstrate repository operations
System.out.println("\n--- Product Operations ---");
System.out.println("Total products: " + productRepo.count());
System.out.println("\nOut of stock products:");
productRepo.findOutOfStock().forEach(System.out::println);
System.out.println("\nProducts between $50 and $200:");
productRepo.findByPriceRange(50, 200).forEach(System.out::println);
System.out.printf("\nTotal inventory value: $%.2f%n", productRepo.calculateTotalInventoryValue());
// π― STATIC METHOD USAGE
System.out.println("\n3. STATIC METHOD VALIDATION:");
System.out.println("Is 'USER-1' valid ID: " + Repository.isValidId("USER-1"));
System.out.println("Is '' valid ID: " + Repository.isValidId(""));
System.out.println("Is null valid ID: " + Repository.isValidId(null));
// π― POLYMORPHISM WITH REPOSITORIES
System.out.println("\n4. POLYMORPHIC REPOSITORY PROCESSING:");
Repository<?>[] repositories = {userRepo, productRepo};
for (Repository<?> repo : repositories) {
System.out.println("\n--- " + repo.getClass().getSimpleName() + " ---");
System.out.println("Entity count: " + repo.count());
System.out.println("Sample entities:");
repo.findAll().stream().limit(2).forEach(System.out::println);
}
// π― BENEFITS AND PATTERNS
System.out.println("\n5. DESIGN PATTERNS ENABLED:");
System.out.println("β
Repository Pattern: Common data access operations");
System.out.println("β
Template Method: Default implementations with hooks");
System.out.println("β
Strategy Pattern: Filtering with predicates");
System.out.println("β
Decorator Pattern: Layering functionality");
System.out.println("\n6. BEST PRACTICES:");
System.out.println("πΉ Use default methods for common implementation");
System.out.println("πΉ Keep default methods simple and focused");
System.out.println("πΉ Use static methods for utility functions");
System.out.println("πΉ Override default methods when optimization needed");
System.out.println("πΉ Document default method behavior clearly");
// π― CLEANUP
System.out.println("\n7. CLEANUP:");
userRepo.deleteAll();
productRepo.deleteAll();
System.out.println("All repositories cleared");
}
}
Output:
=== ADVANCED PATTERNS WITH DEFAULT METHODS ===
1. USER REPOSITORY:
πΎ Saved 3 entities
β
Saved user: Alice Johnson
β
Saved user: Bob Smith
β
Saved user: Carol Davis
--- Default Method Demonstrations ---
Total users: 3
User USER-1 exists: true
Active users:
User{id='USER-1', name='Alice Johnson', email='[email protected]', active=true}
User{id='USER-2', name='Bob Smith', email='[email protected]', active=true}
User{id='USER-3', name='Carol Davis', email='[email protected]', active=true}
π All users deactivated
Active users after deactivation: 0
2. PRODUCT REPOSITORY:
πΎ Saved 4 entities
β
Saved product: Laptop
β
Saved product: Mouse
β
Saved product: Keyboard
β
Saved product: Monitor
--- Product Operations ---
Total products: 4
Out of stock products:
Product{id='P2', name='Mouse', price=$29.99, stock=0}
Products between $50 and $200:
Product{id='P3', name='Keyboard', price=$79.99, stock=5}
Total inventory value: $12569.86
3. STATIC METHOD VALIDATION:
Is 'USER-1' valid ID: true
Is '' valid ID: false
Is null valid ID: false
4. POLYMORPHIC REPOSITORY PROCESSING:
--- UserRepository ---
Entity count: 3
Sample entities:
User{id='USER-1', name='Alice Johnson', email='[email protected]', active=false}
User{id='USER-2', name='Bob Smith', email='[email protected]', active=false}
--- ProductRepository ---
Entity count: 4
Sample entities:
Product{id='P1', name='Laptop', price=$999.99, stock=10}
Product{id='P2', name='Mouse', price=$29.99, stock=0}
5. DESIGN PATTERNS ENABLED:
β
Repository Pattern: Common data access operations
β
Template Method: Default implementations with hooks
β
Strategy Pattern: Filtering with predicates
β
Decorator Pattern: Layering functionality
6. BEST PRACTICES:
πΉ Use default methods for common implementation
πΉ Keep default methods simple and focused
πΉ Use static methods for utility functions
πΉ Override default methods when optimization needed
πΉ Document default method behavior clearly
7. CLEANUP:
ποΈ Deleted 3 entities
ποΈ Deleted 4 entities
All repositories cleared
Key Rules and Resolution
Default Method Rules:
- Class wins over interface
- Most specific interface wins
- Explicit override required for conflicts
- Can call parent with
InterfaceName.super.methodName()
Diamond Problem Resolution:
interface A { default void foo() {} }
interface B extends A { default void foo() {} }
interface C extends A { default void foo() {} }
class D implements B, C {
// β COMPILATION ERROR - must override
@Override
public void foo() {
B.super.foo(); // Choose explicitly
}
}
Best Practices
- Use for backward compatibility - add methods to existing interfaces
- Keep implementations simple - avoid complex logic in defaults
- Document behavior - clearly state what default methods do
- Use static methods for utility functions
- Override when needed - provide class-specific implementations
- Avoid state - default methods shouldn't maintain state
- Consider composition over complex interface hierarchies
Common Use Cases
β Perfect for:
- Utility methods in collections
- Adapter methods in functional interfaces
- Common implementation in frameworks
- Backward compatibility in APIs
- Template methods with hooks
β Avoid for:
- Complex business logic
- State-dependent operations
- Performance-critical code
- When abstraction is unclear
Conclusion
Default methods revolutionized Java interfaces by:
- β Enabling interface evolution without breaking changes
- β Providing common implementation in one place
- β Supporting multiple inheritance of behavior
- β Enabling powerful patterns like repositories and templates
- β Maintaining backward compatibility with existing code
Key Takeaways:
defaultkeyword enables method implementations in interfaces- Classes inherit default methods automatically
- Can be overridden by implementing classes
- Solve diamond problem with explicit overrides
- Enable rich interfaces with common functionality
Remember: Default methods are like having a "starter kit" in your interfaces - implementing classes get useful functionality out of the box but can customize it as needed! ππ
Now you're equipped to design flexible, evolvable interfaces that provide real value to implementing classes!