Until Java 1.7, inside an interface, we could only define public abstract methods and public static final variables. Every method present inside an interface is always public and abstract, whether we declare it or not. Similarly, every variable declared inside an interface is always public, static, and final, whether we declare it or not. With the introduction of default methods in interfaces, it is now possible to include method implementations within interfaces, providing more flexibility and enabling new design patterns.
From Java 1.8 onwards, in addition to these, we can declare default concrete methods inside interfaces, also known as defender methods.
We can declare a default method using the keyword default
as follows:
default void m1() {
System.out.println("Default Method");
}
Interface default methods are by default available to all implementation classes. Based on the requirement, an implementation class can use these default methods directly or override them.
Default Methods in Interfaces Example:
interface ExampleInterface {
default void m1() {
System.out.println("Default Method");
}
}
class ExampleClass implements ExampleInterface {
public static void main(String[] args) {
ExampleClass example = new ExampleClass();
example.m1();
}
}
Default methods are also known as defender methods or virtual extension methods. The main advantage of default methods is that we can add new functionality to the interface without affecting the implementation classes (backward compatibility).
Note: We can’t override Object class methods as default methods inside an interface; otherwise, we get a compile-time error.
Example:
interface InvalidInterface {
default int hashCode() {
return 10;
}
}
Compile-Time Error: The reason is that Object class methods are by default available to every Java class, so it’s not required to bring them through default methods.
Default Method vs Multiple Inheritance
Two interfaces can contain default methods with the same signature, which may cause an ambiguity problem (diamond problem) in the implementation class. To overcome this problem, we must override the default method in the implementation class; otherwise, we get a compile-time error.
Example 1:
interface Left {
default void m1() {
System.out.println("Left Default Method");
}
}
interface Right {
default void m1() {
System.out.println("Right Default Method");
}
}
class CombinedClass implements Left, Right {
public void m1() {
System.out.println("Combined Class Method");
}
public static void main(String[] args) {
CombinedClass combined = new CombinedClass();
combined.m1();
}
}
Example 2:
class CombinedClass implements Left, Right {
public void m1() {
Left.super.m1();
}
public static void main(String[] args) {
CombinedClass combined = new CombinedClass();
combined.m1();
}
}
Differences between Interface with Default Methods and Abstract Class
Even though we can add concrete methods in the form of default methods to the interface, it won’t be equal to an abstract class.
Interface with Default Methods | Abstract Class |
Every variable is always public static final. | May contain instance variables required by child classes. |
Does not talk about the state of the object. | Can talk about the state of the object. |
Cannot declare constructors. | Can declare constructors. |
Cannot declare instance and static blocks. | Can declare instance and static blocks. |
Functional interface with default methods can refer to lambda expressions. | Cannot refer to lambda expressions. |
Cannot override Object class methods. | Can override Object class methods. |
Static Methods in Java 8 Inside Interface
From Java 1.8 onwards, we can write static methods inside an interface to define utility functions. Interface static methods are by default not available to the implementation classes. Therefore, we cannot call interface static methods using an implementation class reference. We should call interface static methods using the interface name.
interface UtilityInterface {
public static void sum(int a, int b) {
System.out.println("The Sum: " + (a + b));
}
}
class UtilityClass implements UtilityInterface {
public static void main(String[] args) {
UtilityInterface.sum(10, 20);
}
}
As interface static methods are not available to the implementation class, the concept of overriding is not applicable. We can define exactly the same method in the implementation class, but it’s not considered overriding.
Example 1:
interface StaticMethodInterface {
public static void m1() {}
}
class StaticMethodClass implements StaticMethodInterface {
public static void m1() {}
}
Example 2:
interface StaticMethodInterface {
public static void m1() {}
}
class StaticMethodClass implements StaticMethodInterface {
public void m1() {}
}
This is valid but not considered overriding.
Example 3:
class ParentClass {
private void m1() {}
}
class ChildClass extends ParentClass {
public void m1() {}
}
This is valid but not considered overriding.
From Java 1.8 onwards, we can write the main()
method inside an interface, and hence we can run the interface directly from the command prompt.
Example:
interface MainMethodInterface {
public static void main(String[] args) {
System.out.println("Interface Main Method");
}
}
At the command prompt:
javac MainMethodInterface.java
java MainMethodInterface
Differences between Interface with Default Methods and Abstract Class
In conclusion, while interfaces with default methods offer some of the functionalities of abstract classes, there are still distinct differences between the two, particularly in terms of handling state, constructors, and method overriding capabilities.
Static Methods Inside Interface
It is important to note that interface static methods cannot be overridden. Here is another example illustrating this concept:
Example:
interface CalculationInterface {
public static void calculate(int a, int b) {
System.out.println("Calculation: " + (a + b));
}
}
class CalculationClass implements CalculationInterface {
public static void calculate(int a, int b) {
System.out.println("Calculation (class): " + (a * b));
}
public static void main(String[] args) {
CalculationInterface.calculate(10, 20); // Calls the interface static method
CalculationClass.calculate(10, 20); // Calls the class static method
}
}
In this example, CalculationInterface.calculate()
and CalculationClass.calculate()
are two separate methods, and neither overrides the other.
Main Method in Interface
From Java 1.8 onwards, we can write a main()
method inside an interface and run the interface directly from the command prompt. This feature can be useful for testing purposes.
Differences between Interface with Default Methods and Abstract Class (Continued)
In conclusion, while interfaces with default methods offer some of the functionalities of abstract classes, there are still distinct differences between the two, particularly in terms of handling state, constructors, and method overriding capabilities.
Static Methods Inside Interface (Continued)
It is important to note that interface static methods cannot be overridden. Here is another example illustrating this concept:
Example:
interface CalculationInterface {
public static void calculate(int a, int b) {
System.out.println("Calculation: " + (a + b));
}
}
class CalculationClass implements CalculationInterface {
public static void calculate(int a, int b) {
System.out.println("Calculation (class): " + (a * b));
}
public static void main(String[] args) {
CalculationInterface.calculate(10, 20); // Calls the interface static method
CalculationClass.calculate(10, 20); // Calls the class static method
}
}
In this example, CalculationInterface.calculate()
and CalculationClass.calculate()
are two separate methods, and neither overrides the other.
Main Method in Interface
From Java 1.8 onwards, we can write a main()
method inside an interface and run the interface directly from the command prompt. This feature can be useful for testing purposes.
Example:
interface ExecutableInterface {
public static void main(String[] args) {
System.out.println("Interface Main Method");
}
}
To compile and run the above code from the command prompt:
javac ExecutableInterface.java
java ExecutableInterface
Additional Points to Consider
- Multiple Inheritance in Interfaces:
- Interfaces in Java support multiple inheritance, which means a class can implement multiple interfaces. This is particularly useful when you want to design a class that conforms to multiple contracts.
- Resolution of Default Methods:
- If a class implements multiple interfaces with conflicting default methods, the compiler will throw an error, and the class must provide an implementation for the conflicting methods to resolve the ambiguity.
Example:
interface FirstInterface {
default void show() {
System.out.println("FirstInterface Default Method");
}
}
interface SecondInterface {
default void show() {
System.out.println("SecondInterface Default Method");
}
}
class ConflictResolutionClass implements FirstInterface, SecondInterface {
@Override
public void show() {
System.out.println("Resolved Method");
}
public static void main(String[] args) {
ConflictResolutionClass obj = new ConflictResolutionClass();
obj.show(); // Calls the resolved method
}
}
3. Functional Interfaces with Default Methods:
- A functional interface is an interface with a single abstract method, but it can still have multiple default methods. This combination allows you to provide a default behavior while still adhering to the functional programming paradigm.
@FunctionalInterface
interface FunctionalExample {
void singleAbstractMethod();
default void defaultMethod1() {
System.out.println("Default Method 1");
}
default void defaultMethod2() {
System.out.println("Default Method 2");
}
}
class FunctionalExampleClass implements FunctionalExample {
@Override
public void singleAbstractMethod() {
System.out.println("Implemented Abstract Method");
}
public static void main(String[] args) {
FunctionalExampleClass obj = new FunctionalExampleClass();
obj.singleAbstractMethod();
obj.defaultMethod1();
obj.defaultMethod2();
}
}
Summary
Java 8 introduced significant enhancements to interfaces, primarily through the addition of default and static methods. These changes allow for more flexible and backward-compatible API design. Here are the key points:
- Default Methods: Provide concrete implementations in interfaces without affecting existing implementing classes.
- Static Methods: Allow utility methods to be defined within interfaces.
- Main Method in Interfaces: Enables testing and execution of interfaces directly.
- Conflict Resolution: Requires explicit resolution of conflicting default methods from multiple interfaces.
- Functional Interfaces: Can have default methods alongside a single abstract method, enhancing their utility in functional programming.
These features make Java interfaces more powerful and versatile, facilitating more robust and maintainable code design.