Published: June 15, 2016
Introduction
Java 8 introduced a significant change to interfaces by allowing the definition of default methods. Prior to Java 8, interfaces could only declare methods, and all classes implementing those interfaces had to provide an implementation. This was quite limiting in terms of maintaining backward compatibility while adding new methods to interfaces.
Default methods in interfaces enable you to add methods with implementations in interfaces without breaking the existing implementations of that interface. In this tutorial, we will:
- Understand what default methods are.
- Learn how to use them in your code.
- Explore some practical examples and use cases.
What Are Default Methods?
Default methods allow you to define methods in interfaces with a body. This is important because it enables you to add new functionality to an interface without affecting the classes that implement that interface. The method is “default” because it provides a default implementation, which can be overridden by implementing classes if needed.
Syntax:
public interface MyInterface {
// A default method in the interface
default void printMessage() {
System.out.println("This is a default method!");
}
}
In the example above, printMessage()
is a default method that provides a default implementation. Classes implementing MyInterface
can either use this default implementation or override it.
Why Use Default Methods?
Default methods were introduced to solve two major problems:
- Backward Compatibility: You can add new methods to interfaces without breaking existing code.
- Multiple Inheritance: Java doesn’t support multiple inheritance, but default methods allow classes to inherit methods from multiple interfaces.
Using Default Methods in Interfaces
Let’s see how to use default methods in interfaces with a simple example.
Example 1: A Simple Default Method
public interface Greeter {
// Default method
default void greet() {
System.out.println("Hello from Greeter!");
}
}
public class Person implements Greeter {
// The class doesn't need to implement greet() because it's already provided
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.greet(); // Outputs: Hello from Greeter!
}
}
Explanation: The Greeter
interface provides a default implementation for the greet()
method. The Person
class doesn’t need to implement greet()
, as the interface provides a default method.
Overriding Default Methods
If you want to customize the behavior of a default method in a class, you can simply override it, just like you would override a regular method in a class.
Example 2: Overriding a Default Method
public interface Greeter {
// Default method
default void greet() {
System.out.println("Hello from Greeter!");
}
}
public class FriendlyPerson implements Greeter {
// Overriding the greet() method
@Override
public void greet() {
System.out.println("Hello, I’m a friendly person!");
}
}
public class Main {
public static void main(String[] args) {
FriendlyPerson person = new FriendlyPerson();
person.greet(); // Outputs: Hello, I’m a friendly person!
}
}
Explanation: In this case, FriendlyPerson
overrides the greet()
method to provide a custom greeting.
Default Methods in Multiple Interfaces
Java allows a class to implement multiple interfaces, and if the interfaces provide conflicting default methods, the class is required to resolve the conflict.
Example 3: Resolving Conflicts Between Default Methods
public interface Greeter {
// Default method
default void greet() {
System.out.println("Hello from Greeter!");
}
}
public interface PoliteGreeter {
// Default method
default void greet() {
System.out.println("Good day from PoliteGreeter!");
}
}
public class Person implements Greeter, PoliteGreeter {
// Resolving the conflict by overriding greet() in the class
@Override
public void greet() {
System.out.println("Hello, I am a person and I greet in my way!");
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.greet(); // Outputs: Hello, I am a person and I greet in my way!
}
}
Explanation: The Greeter
and PoliteGreeter
interfaces both have a default greet()
method. The Person
class resolves the conflict by overriding the greet()
method, providing its own implementation.
Best Practices for Using Default Methods
- Use Default Methods for Backward Compatibility: Default methods are a great tool when you want to add new methods to an interface without breaking existing code that implements that interface.
- Don’t Overuse Default Methods: While default methods can make interfaces more powerful, you should use them sparingly. Overuse of default methods can lead to confusion and a poor design.
- Keep Implementations Simple: The primary goal of default methods is to provide common functionality that can be shared by all implementing classes. Try to avoid complex logic in default methods.
When to Avoid Default Methods
Default methods should not be used in situations where:
- The method should only be defined in classes, not in interfaces.
- You want to enforce all implementing classes to provide their own implementation of the method.
In these cases, it’s better to leave the method abstract in the interface and require all implementing classes to define their own implementation.
Conclusion
Java 8’s default methods are a powerful tool that enables you to add new functionality to interfaces without breaking existing code. They offer a way to solve the backward compatibility problem and provide more flexibility in interface design. However, they should be used carefully and in moderation to avoid cluttering your codebase.
In the next tutorial, we will dive into Implementing Your First Java 8 Stream Pipeline and see how streams work in Java 8.