Default Methods In Java 8

Default Methods In Java

Introduction

Default methods in Java, introduced in Java 8, revolutionized the way interfaces are used in the language. They allow interfaces to have method implementations, providing a new level of flexibility and backward compatibility. In this article, we will delve into default methods in Java, understand their purpose, and explore how they enhance the functionality of interfaces.

What are default methods in Java ?

Default methods in Java are methods defined within an interface with a default keyword. These methods have an implementation in the interface itself, allowing interfaces to provide method definitions without requiring implementing classes to override them. This feature was introduced to enhance the evolution of interfaces in Java without breaking existing implementations.

Example of Default Methods in Java:

interface Vehicle {
    default void start() {
        System.out.println("Starting the vehicle");
    }

    default void stop() {
        System.out.println("Stopping the vehicle");
    }
}

class Car implements Vehicle {
    // No need to implement start() and stop() methods
}

public class DefaultMethodsExample {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();
        car.stop();
    }
}

Resolving the Diamond Problem with Default Methods

The Diamond Problem refers to a scenario in multiple inheritance where a class inherits from two classes that have a common ancestor. This can lead to ambiguity in method resolution if both parent classes have a method with the same signature.

Let’s consider an example to illustrate how Java resolves the Diamond Problem with default methods:

Suppose we have two interfaces, A and B, both of which declare a default method called show():

interface A {
    default void show() {
        System.out.println("Default method from interface A");
    }
}

interface B {
    default void show() {
        System.out.println("Default method from interface B");
    }
}

Now, let’s create a class C that implements both interfaces A and B:

class C implements A, B { }

If we try to compile this code, we’ll encounter a compilation error due to the Diamond Problem. Both interfaces A and B have a default method show(), and the class C inherits both of them.

Above problem can be handled by following ways.

Solution 1:

interface A {
    default void show() {
        System.out.println("Default method from interface A");
    }
}

interface B {
    default void show() {
        System.out.println("Default method from interface B");
    }
}

class C implements A, B {
    // Resolving the conflict by providing our own implementation
    public void show() {
        System.out.println("Implementation in class C");
    }
}

Solution 2:

interface A {
    default void show() {
        System.out.println("Default method from interface A");
    }
}

interface B {
    default void show() {
        System.out.println("Default method from interface B");
    }
}

class C implements A, B {
    // Providing our own implementation for show()
    public void show() {
        // Explicitly invoking A's default method
        A.super.show();
    }
}

Built in interfaces in Java in which default methods were introduced:

In Java, default methods were introduced in several built-in interfaces to provide backward compatibility and enable the addition of new methods without breaking existing implementations. Some of the notable built-in interfaces in Java 8 that were enhanced with default methods include:

Iterable Interface:

  • Introduced default method: forEach
  • Purpose: Provides a default implementation for iterating over elements of a collection.

Example :

List<String> list = Arrays.asList("Apple", "Banana", "Orange"); list.forEach(System.out::println); // prints each element of the list

Collection Interface:

  • Introduced default methods: stream, parallelStream, removeIf, forEach, etc.
  • Purpose: Provides default implementations for stream operations, removal based on a predicate, and iteration over elements.

Example :

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); 
numbers.removeIf(n -> n % 2 == 0); // removes even numbers

Comparator Interface:

  • Introduced default methods: reversed, thenComparing, etc.
  • Purpose: Provides default implementations for sorting and comparing objects.

Example :

Comparator<String> byLength = Comparator.comparing(String::length); 
byLength = byLength.reversed(); // sorts strings by length in descending order

Map Interface:

  • Introduced default methods: forEach, getOrDefault, putIfAbsent, remove, etc.
  • Purpose: Provides default implementations for various map operations, including iteration, retrieval, and modification.

Example :

Map<String, Integer> scores = new HashMap<>(); 
scores.put("Alice", 90); scores.put("Bob", 85); 
scores.forEach((name, score) -> System.out.println(name + ": " + score)); 
// prints each entry

CharSequence Interface:

  • Introduced default methods: codePoints, chars, codePointAt, codePointBefore, etc.
  • Purpose: Provides default implementations for working with character sequences.

Example :

java String str = "Hello"; 
str.chars().forEach(System.out::println); // prints ASCII values of characters

Benefits of Default Methods:

  1. Backward Compatibility: Default methods allow interfaces to evolve without breaking existing implementations. Classes that implement interfaces with default methods do not need to provide implementations for these methods unless they want to override them.
  2. Interface Extension: Default methods enable interfaces to have method implementations, blurring the line between interfaces and abstract classes. This provides more flexibility in designing interfaces with shared behavior.
  3. Code Reusability: Default methods promote code reusability by allowing multiple classes to share common method implementations defined in interfaces. This reduces code duplication and improves maintainability.

Best Practices for Using Default Methods in Java:

  1. Use Default Methods Sparingly: Default methods should be used judiciously and only for methods that have sensible default implementations applicable to all implementing classes.
  2. Avoid Method Name Conflicts: Be cautious when adding default methods to interfaces to avoid method name conflicts with implementing classes or other interfaces. Prefer unique and descriptive method names.
  3. Document Default Method Behavior: Clearly document the behavior and intended purpose of default methods in interface documentation to guide implementing classes and maintain code readability.

If you are preparing for Java 8 interviews then checkout this.

Conclusion

Default methods in Java represent a significant evolution in interface design, offering greater flexibility, backward compatibility, and API extensibility. By embracing default methods, developers can create more robust and expressive APIs, simplify interface evolution, and enhance code maintainability. Whether you’re a seasoned Java developer or just starting your journey, leverage the power of default methods to unlock new possibilities in Java programming.

Share this article with tech community
WhatsApp Group Join Now
Telegram Group Join Now

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *