Java 8 default methods in interfaces

Java 8 implemented a new type of method into interfaces, called default methods. Default methods in Java interfaces allow to create methods with body, which classes can either choose to inherit or extend. In this article, I am explaining how to use default methods in Java interfaces.

 Implementing interface default method

Implementing a default method is as simple as putting the default keyword before method name and providing method body enclosed within {curly braces}.

Consider the code below:

public class Prototyping {
   public static void main(String[] args) {
      Greeter greeter1 = new BritishGreeter();
      Greeter greeter2 = new AustralianGreeter();
      Greeter greeter3 = new CanadianGreeter();
      Greeter greeter4 = new BavarianGreeter();

      greeter1.sayHello(); // prints "Good morning!"
      greeter2.sayHello(); // prints "Good morning!"
      greeter3.sayHello(); // prints "Good morning!"
      greeter4.sayHello(); // prints "Griaß di!"
   }
}

interface Greeter {
   default void sayHello() {
      System.out.println("Good morning!");
   }
}
class BritishGreeter implements Greeter {
   // sayHello is not overridden, therefore it's inherited
}

class AustralianGreeter implements Greeter {
   // sayHello is not overridden, therefore it's inherited
}

class CanadianGreeter implements Greeter {
   // sayHello is not overridden, therefore it's inherited
}

class BavarianGreeter implements Greeter {    
   // Overriding sayHello with custom implementation    
   public void sayHello() {
       System.out.println("Griaß di!");    
   } 
}

In the code above we have Greeter interface that is implemented in BritishGreeterAustralianGreeterCanadianGreeter and BavarianGreeter classes. The purpose of those classes is to display a greeting message in local language.

As most of our classes will be displaying greeting in English language, it makes no sense to write exactly the same code in multiple classes. In this case, using a default method in the interface is a great solution. Instead of implementing sayHello() in the interface as an abstract method (that has no body and must be overridden), we have implemented this method as a default method that comes with default implementation.

The classes that inherit this interface can now decide to either inherit the sayHello() with its default implementation (displaying greeting in English) or to override it with custom code.

Now when you take a look at the code above again, you will notice that we had to override the sayHello() method only once, for the BavarianGreeter class. All the other classes could inherit the default implementation, therefore greatly reducing amount of code needed to be written (and repeated).

Interface evolution

A great thing about interface default methods is that they make interface evolution much easier to implement. With default methods, we can add new methods to interfaces without worrying about classes that do not implement the new method.

Let’s say that in your big project you have a Car interface that has only one method: void refuel(). You have created many classes thorough your code that implement this interface. After some time you have decided that you need another method in your Car interface: void recharge(). Now there is an issue, because you have a lot of classes that implement the Car interface. Before Java 8 and default methods you would have to either implement void recharge() in all the classes that implement this interface, or you would have to abandon the idea with new method at all. But with Java 8 and default methods you can implement void recharge() as a default method, retaining compatibility with all your existing code.

Consider the code below:

public class Prototyping {
   public static void main(String[] args) {
      Car impala = new Impala64();
      impala.recharge(); // recharge method is inherited from Car interface

      Car bmw = new BMWi8();
      bmw.recharge(); // Executes overridden recharge() method in BMWi8 class
   }
}

interface Car {
   void refuel();

   default void recharge() {
      // Recharging logic...
      // or  throw new java.lang.UnsupportedOperationException("recharge() not implemented");
   }
}

class Impala64 implements Car {
   public void refuel() {
      // Refueling logic...
   }

   // Notice lack of recharge() implementation
}

class BMWi8 implements Car {
   public void refuel() {
      // Refueling logic...
   }

   public void recharge() {
      // Recharging logic...
   }
}

In the code above, the Car interface has abstract refuel() method and default recharge() method.  The refuel() method must be implemented in all classes that implement the Car interface, while the recharge() default method doesn’t have to. It comes with a default implementation contained within its body. It can be either overridden or inherited by classes that implement the interface.

The Impala64 class doesn’t implement recharge() method, therefore it inherits it from the interface. Errors about not implemented method will NOT arise.

The BMWi8 class implements recharge() method, therefore it is overridden with BMWi8 class own implementation.

Thanks to this mechanism, we don’t have to worry that our new methods will break any old code.

Summary

As you can see, default methods in Java interfaces are very easy to implement and to use. They help to reduce amount of code that has to be written (and repeated) and make interface evolution much more easy to implement, retaining compatibility with existing code.

Learn more about Java