What Are Default Methods in Java 8

Default methods were added in Java 8 to allow adding of new methods to interface without breaking existing implementing classes.  Using default methods we can add functionality to existing classes which implemented a common interface.

Note – All the Java source files in this project can be found at https://github.com/freetipscentral/java8-defaultmethod

Here is one example of Default method

Lets assume that we have a GenericRemote interface and two classes which implement the interface.

package com.freetipscentral.defaultmethod;

public interface GenericRemote {
  
  public void startDevice();
}

 

package com.freetipscentral.defaultmethod;

public class TVRemote implements GenericRemote {
  public void startDevice(){
    System.out.println("TV Started");
  }
}
package com.freetipscentral.defaultmethod;

public class MusicSystemRemote implements GenericRemote {
  public void startDevice(){
    System.out.println("Music System Started");
  }
}

In the above example you can see that two classes TVRemote and MusicSystemRemote implementing the interface GenericRemote hence they also implement the method startDevice().

Now consider the situation where in you need to add one more method stopDevice() to the interface GenericRemote interface.

Before Java 8 as soon as you add that method to the GenericRemote interface all the classes below will start having compilation problems.  You can solve the compilation problems by implementing the method in all the classes which implement this interface.  Same happens in Java 8 also.

But Java 8 has a more elegant solution for this.  That is called Default method.  Default method are a way to add a default implementation of the interface method on the interface itself.  That way if the interface undergoes changes after it has been implemented by many classes, those classes will not have compilation errors.

How to Add a Default Implementation Method

To add a default implementation add the keyword default at the start of the method signature and then implement the method.  Here is how the above implementations will look like after adding the default method.  Notice how the implementing classes have not undergone any changes.

package com.freetipscentral.defaultmethod;

public interface GenericRemote {
 
 public void startDevice();
 
 default public void stopDevice() {
 System.out.println("Default Stop Device");
 }
}
package com.freetipscentral.defaultmethod;

public class TVRemote implements GenericRemote {
  public void startDevice(){
    System.out.println("TV Started");
  }
}
package com.freetipscentral.defaultmethod;

public class MusicSystemRemote implements GenericRemote {
  public void startDevice(){
    System.out.println("Music System Started");
  }
}

Do The Implementing Classes Have Choice to Overriding Default Methods

Yes implementing classes can always override the default method and add their own implementation.  That way they can have the best of both worlds.

For example TVRemote class can always override the default implementation as below.

package com.freetipscentral.defaultmethod;

public class TVRemote implements GenericRemote {
  public void startDevice(){
    System.out.println("TV Started");
  }
  
  @Override
  public void stopDevice() {
    System.out.println("TV Stopped");
  }
}

 

Why Default methods were added to Java 8

Default methods were added to Java so that backwards compatibility could be maintained with older collection framework.  One of the new method added to the Collection interface is forEach.  forEach is an internal iterator which has been  added to Iterable interface in Java 8.  Collection interface extends Iterable interface.  This method helps in iterating through the objects in the collection.  For example consider the following example –

Here is what is being done in the code.

  1. Create a list of Students.
  2. Add Student objects to the list
  3. Iterate through the list and print the students.

If you notice carefully we are using forEach which is an internal iterator.  Since this is new addition to the Collection framework  all the previously written codes (running in millions of lines) which used Collection interface such as List or Set would have failed.  Hence Java 8 added the default method so that the code remains backward compatible.  If you see the Oracle documentation on Iterable interface you will see that forEach is a default method.  Here is the link to Oracle documentation on Iterable interface.

package com.freetipscentral.defaultmethod;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import com.freetipscentral.defaultmethod.domain.Student;

public class StudentManager {
  public static void main(String args[]) {
    List<Student> students = new ArrayList<Student>();
    
    //Create Students and add to list
    Student student1 = new Student("Karen",1);
    Student student2 = new Student("Rahul",2);
    students.add(student1);
    students.add(student2);
    
    //Iterate through the list and print the student objects
    students.forEach(new Consumer<Student>() {
      public void accept(Student s) {
        System.out.println(s);
      }
    });
  }
}

What happens when a class inherits two interfaces with default methods with same name

If a class implements two interfaces with same default method name then it will cause compile error in the class.  The solution is to implement the default method in the class.

What Happens When an Interface Extends Another Interface With Default Method

If an interface extends another interface which has default method then the interface and its implementing classes also have access to the default method.

However the child interface can also make it abstract.  It the child interface makes it abstract then the implementing classes of the child interface have to implement the method.  Here is an example of how child interface can make the default method abstract.  All classes implementing MobileRemote need to implement stopDevice() method.

package com.freetipscentral.defaultmethod;

public interface MobileRemote extends GenericRemote {
  public void stopDevice();
}

The child interface can also override the default interface with its own default method.  In this case the implementing classes of the child interface can use the default method of the child interface.   Here is an example of how child interface can override the default method.

package com.freetipscentral.defaultmethod;

public interface CarRemote extends GenericRemote {
  default public void stopDevice() {
    System.out.println("Stop the Car");
  }
}