Traditionally in Java, we could have only method definition and not the actual implementation in an Interface. Now with Java 8, we can provide method implementation to Interfaces. These methods are called default methods:
public interface Shape { default void drawShape() { System.out.print("Drawing shape"); } }
Why Default Methods are required:
Imagine we have an Interface and we need to add new methods in that Interface. Before Java 8, whenever an existing interface defines new method, all the implementing classes need to override the new method, even if the method is not required for the existing implementing class.
But with Java 8 and latter, we can simply add new methods to interface with default keyword before the method signature and all the old implementation of the interface can remain unchanged and work properly.
How default methods work:
A class takes the closest and most specific implementation of a default method, which include one of the below:
1. A class can override the default method of implemented interface to provide its own implementation.
class Triangle implements Shape { public void drawShape() { System.out.print("Drawing Triangle"); } } public class Test { public static void main(String[] args) { new Triangle().drawShape(); } }
When the Test class will run, it will print “Drawing Triangle”.
2. When a class extends a parent class which have the implementation for the default method, it will inherit the implementation from the parent class.
class Triangle implements Shape { public void drawShape() { System.out.print("Drawing Triangle"); } } class RightTriangle extends Triangle { } public class Test { public static void main(String[] args) { new RightTriangle().drawShape(); } }
After running the main class Test, “Drawing Triangle” will be printed.
3. When a class implements a sub-Interface(an Interface which extends another Interface) and both the Interfaces provide the default method with same signature, the class will inherit from sub-Interface .
interface Rectangle extends Shape { default void drawShape() { System.out.print("Drawing Rectangle"); } } class Square implements Rectangle { } public class Test { public static void main(String[] args) { new Square().drawShape(); } }
After running the main class Test, “Drawing Rectangle” will be printed.
4. When a class implements an Interface with default method and extends a class which have method with same name, it will get the implementation from the extended class.
public class TwoDShape { public void drawShape() { System.out.print("Drawing TwoDShape"); } } public class Circle extends TwoDShape implements Shape { } public class Test { public static void main(String[] args) { new Circle().drawShape(); } }
After running the main class Test, “Drawing TwoDShape” will be printed.
5. When the implementing class does not override the default method, it will get the default implementation from the Interface.
class Circle implements Shape { } public class Test { public static void main(String[] args) { new Circle().drawShape(); } }
After running the main class Test, “Drawing Shape” will be printed.
After extending an Interface, we can do following:
1. Not mention the default method at all and the sub-Interface will inherit from the parent Interface:
The implementing class will get the default implementation from parent Interface.
interface ThreeDShape extends Shape { } public class Sphere implements ThreeDShape { } public class Test { public static void main(String[] args) { new Sphere().drawShape(); } }
After running the main class Test, “Drawing Shape” will be printed.
2. Redeclare the default method and make it abstract.
The implementing class will have to provide the implementation.
interface ThreeDShape extends Shape { void drawShape(); } class Sphere implements ThreeDShape{ public void drawShape() { System.out.print("Drawing Sphere"); } } public class Test { public static void main(String[] args) { new Sphere().drawShape(); } }
After running the main class Test, “Drawing Sphere” will be printed.
3. Redefine the default method and override it, providing its own implementation.
The implementing class will inherit from the sub-Interface.
interface ThreeDShape extends Shape { default void drawShape() { System.out.print("Drawing ThreeDShape"); } } class Sphere implements ThreeDShape{ } public class Test { public static void main(String[] args) { new Sphere().drawShape(); } }
After running the main class Test, “Drawing ThreeDShape” will be printed.
What will happen if a class implements two interface with same default method:
This situation will cause compilation error.
Below are two Interfaces IndianFood and ContinentalFood and both have default method cookingFood(). Now if a class TodayMeal implements both Interfaces, it will get error message “Duplicate default methods named cookingFood with the parameters () and () are inherited from the types ContinentalFood and IndianFood” while compiling the code.
interface IndianFood { default void cookingFood() { System.out.println("Cokking Indian food"); } } interface ContinentalFood { default void cookingFood() { System.out.println("Cokking Continental food"); } } public class TodayMeal implements IndianFood, ContinentalFood { }
To resolve the issue the class needs to override the default method and call the required method explicitly.
public class TodayMeal implements IndianFood, ContinentalFood { @Override public void cookingFood() { IndianFood.super.cookingFood(); } }
Limitation of default methods:
default method can not override the methods from java.lang.Object.
very informative blog and very nicely explained.