Before Java 8 your interfaces could contain method declarations, but no implementation code. One of the new features of Java 8 is the option to provide default implementations for methods right in your interface code itself.

Backward Compatibility

The main reason for adding this functionality was to allow the Java 8 team to add new methods to the Collections API (e.g. the forEach() method). Without this, existing third-party libraries that already implement the Java collections API would not have worked with Java 8. These vendors would have been forced to upgrade their code for Java 8. Even worse, they would have to create and maintain separate versions if they wanted to continue supporting their non-Java 8 users.

Instead of breaking backward compatibility, the Java 8 team came up with a clever solution. Allow developers to include a default implementation for their methods right in the interface itself — very similar to abstract classes. For example, the Iterable class now declares a default implementation for the forEach method.

default void forEach(Consumer<? super T> action) 1 default void forEach ( Consumer < ? super T > action )

Default Methods in Action

Let’s understand how you can use default methods with an example. Assume that you have an interface called NamedPerson.

public interface NamedPerson { String firstName(); String lastName(); } 1 2 3 4 public interface NamedPerson { String firstName ( ) ; String lastName ( ) ; }

You also have two classes ( Student and Contact ) that implement this interface.

class Student implements NamedPerson { private String fName, lName; public Student(String first, String last) { this.fName = first; this.lName = last; } public String firstName() { return fName; } public String lastName() { return lName; } } class Contact implements NamedPerson { private String fName, lName; public Contact(String first, String last) { this.fName = first; this.lName = last; } public String firstName() { return fName; } public String lastName() { return lName; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Student implements NamedPerson { private String fName , lName ; public Student ( String first , String last ) { this . fName = first ; this . lName = last ; } public String firstName ( ) { return fName ; } public String lastName ( ) { return lName ; } } class Contact implements NamedPerson { private String fName , lName ; public Contact ( String first , String last ) { this . fName = first ; this . lName = last ; } public String firstName ( ) { return fName ; } public String lastName ( ) { return lName ; } }

Now, suppose you wanted to add a method that returns the full name of the person long after the interface had been released and implemented by other developers in their own apps. You would start by adding the new method as usual.

public interface NamedPerson { String firstName(); String lastName(); String fullName(); } 1 2 3 4 5 public interface NamedPerson { String firstName ( ) ; String lastName ( ) ; String fullName ( ) ; }

If you left the interface like this, both your Student and Contact classes would have to change. Plus any developer implementing this interface would also have to change their class.

To avoid this in Java 8, you just need to add a default implementation to your fullName() method in the NamedPerson interface itself.

public interface NamedPerson { String firstName(); String lastName(); default String fullName() { return firstName() + " " + lastName(); } } 1 2 3 4 5 6 7 8 public interface NamedPerson { String firstName ( ) ; String lastName ( ) ; default String fullName ( ) { return firstName ( ) + " " + lastName ( ) ; } }

Testing Default Methods

You can test this with a simple program.

import java.util.Arrays; import java.util.List; class Main { public static void main(String[] args) { List<NamedPerson> persons = Arrays.asList( new Student("Albert", "Einstein"), new Contact("Albert", "Pinto")); for(NamedPerson person : persons) { System.out.println(person.fullName()); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java . util . Arrays ; import java . util . List ; class Main { public static void main ( String [ ] args ) { List <NamedPerson> persons = Arrays . asList ( new Student ( "Albert" , "Einstein" ) , new Contact ( "Albert" , "Pinto" ) ) ; for ( NamedPerson person : persons ) { System . out . println ( person . fullName ( ) ) ; } } }

This gives the following output.

Albert Einstein Albert Pinto 1 2 Albert Einstein Albert Pinto

Of course, you can always override fullName() at any time. For example, if you wanted to use the format “Last Name, First Name” in the Contact class.

class Contact implements NamedPerson { private String fName, lName; public Contact(String first, String last) { this.fName = first; this.lName = last; } @Override public String firstName() { return fName; } @Override public String lastName() { return lName; } @Override public String fullName() { return lastName() + ", " + firstName(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Contact implements NamedPerson { private String fName , lName ; public Contact ( String first , String last ) { this . fName = first ; this . lName = last ; } @Override public String firstName ( ) { return fName ; } @Override public String lastName ( ) { return lName ; } @Override public String fullName ( ) { return lastName ( ) + ", " + firstName ( ) ; } }

Running the program now gives you the following output.

Albert Einstein Pinto, Albert 1 2 Albert Einstein Pinto , Albert

As you can see, the Student class is using the default implementation from NamedPerson while the Contact class is using its own custom implementation.

Multiple Inheritance Conflicts

It is possible for your classes to implement two interfaces that both supply default implementations of the same method. For example, suppose you have another interface named AddressBookEntry .

public interface AddressBookEntry { String givenName(); String familyName(); default String fullName() { return givenName() + " " + familyName(); } } 1 2 3 4 5 6 7 8 public interface AddressBookEntry { String givenName ( ) ; String familyName ( ) ; default String fullName ( ) { return givenName ( ) + " " + familyName ( ) ; } }

You want the Contact class to implement both the NamedPerson and AddressBookEntry interfaces.

class Contact implements NamedPerson, AddressBookEntry { private String fName, lName; public Contact(String first, String last) { this.fName = first; this.lName = last; } @Override public String firstName() { return fName; } @Override public String lastName() { return lName; } @Override public String givenName() { return fName; } @Override public String familyName() { return lName; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Contact implements NamedPerson , AddressBookEntry { private String fName , lName ; public Contact ( String first , String last ) { this . fName = first ; this . lName = last ; } @Override public String firstName ( ) { return fName ; } @Override public String lastName ( ) { return lName ; } @Override public String givenName ( ) { return fName ; } @Override public String familyName ( ) { return lName ; } }

However, your code would not compile. The compiler would throw the following error since it can’t figure out which default implementation to use.

Contact.java:1: error: class Contact inherits unrelated defaults for fullName() from types NamedPerson and AddressBookEntry class Contact implements NamedPerson, AddressBookEntry 1 2 3 Contact . java : 1 : error : class Contact inherits unrelated defaults for fullName ( ) from types NamedPerson and AddressBookEntry class Contact implements NamedPerson , AddressBookEntry

Resolving this error is quite simple. You just need to provide a custom implementation of fullName() in the Contact class:

class Contact implements NamedPerson, AddressBookEntry { private String fName, lName; public Contact(String first, String last) { this.fName = first; this.lName = last; } @Override public String firstName() { return fName; } @Override public String lastName() { return lName; } @Override public String givenName() { return fName; } @Override public String familyName() { return lName; } @Override public String fullName() { return lName + ", " + fName; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Contact implements NamedPerson , AddressBookEntry { private String fName , lName ; public Contact ( String first , String last ) { this . fName = first ; this . lName = last ; } @Override public String firstName ( ) { return fName ; } @Override public String lastName ( ) { return lName ; } @Override public String givenName ( ) { return fName ; } @Override public String familyName ( ) { return lName ; } @Override public String fullName ( ) { return lName + ", " + fName ; } }