Close

What default methods are and why do we need them?

[Last Updated: Nov 24, 2018]

It was desirable to allow existing libraries to evolve on interface level without breaking the existing implementations. Specially it was needed to make use of new Lambda expression feature. It would be disappointing to add a language feature that enables developers to write better libraries while at the same time not extending the core libraries to take advantage of that feature. For example Collection interfaces needed to be modified to add new bulk operations, which should make use of functional interfaces as parameter, so that we can use Lambda expressions in the client code. Obvious candidates include the addition of methods like forEach, filter, map, and reduce in the Collections classes .

Java 8 provides default and static methods as solution of above problem. Now it's possible to add methods in an interfaces with body.


Kick Start Example:

public interface Employee {

    String getName();
    String getDept();
    BigDecimal getSalary();

    default String getEmployeeInfo() {
        return "Name: " + getName() +
                ", Dept: " + getDept() +
                ", Salary " + getSalary();
    }

    static String getEmployeeInfo(List<Employee> employeeList) {
        String info = "";
        for (Employee employee : employeeList) {
            info += employee.getEmployeeInfo() + "\n";
        }
        return info;
    }
}

Overriding default methods

The implementing classes are not force to implement default methods but they rather have an option to override them.

Method Dispatch

Runtime dispatch of default methods is slightly different from that of ordinary interface method invocation. Invoking a method on a class instance, is searched in that class first, then superclass and so on until we reach java.lang.Object. Searched is stop where implementation of the method is found and finally it's get called. For default methods, first class hierarchy is search as usual. If no implementation found for the target interface method then all implementing interfaces are search for default methods.

Conflicting defaults

What if two interfaces which are being implemented by a class have the same default method signature? It's a compile time error: class XYZ inherits unrelated defaults for someDefaultMethod() .... To fix that we have to override the method.

Example project

In following project example we have extended our previous example to demonstrate different concepts related to the default methods. The class DefaultEmployeeEx has to implement getBonus method as it has been defined as default methods in two implementing interfaces: Employee and BonusCalculator. Here we have to override getBonus to specify which default implementation we are interested in. In our case we chose BonusCalculator.super.getBonus(). If we remove this method we will get this compile time error

com.logicbig.example.DefaultEmployeeEx inherits unrelated defaults for getBonus() from types
com.logicbig.example.Employee and com.logicbig.example.BonusCalculator

Dependencies and Technologies Used:

  • JDK 1.8
  • Maven 3.0.4

Java 8 - Default method example Select All Download
  • default-method-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • Employee.java


    Resolution Rules

    Following rules are applied when overriding methods:

    1. Classes win over interfaces. If a class in the superclass chain has a declaration for the target method (concrete or abstract), there's no ambiguity.
    2. More specific interfaces win over less specific ones. Here specificity means subtyping. A default from List wins over a default (having same signature) from Collection.

    If there is no unique winner according to the above rules, concrete classes must disambiguate manually. Like we did in our example by overriding getBonus() in DefaultEmployeeEx


    Multiple Inheritance of Behavior

    As we saw in above example, default methods provide a way to inherit multiple implementation of methods which is similar to multiple inheritance. But that's limited to behavior only and not the state as we cannot add instance variable to interfaces and we shouldn't be allowed to do that, otherwise there will be no difference between an abstract class and interface then.


    In above example, we are extending DefaultEmployee from DomainObject class. Imagine if we don't have default methods and still want to generalize the two methods getBonus() and getEmployeeInfo(), then possibly we would be creating an intermediate class say AbstractEmployee extends DomainObject and putting the two concrete methods there. Conceptually those methods got nothing to do for being in DomainObject or it's abstract subclass. They should be isolated. Hence we have a more generic and clear way to design our hierarchies by adding behavior as default methods.

    Also checkout this swing example, where it shows how to add new functionality in default methods hence achieving multiple inheritance.


    Why just don't use Utility methods?

    One old way to achieve the same behavior is to create utility methods (getBonus(..) and getEmployeeInfo(..)), say in a class Employees and pass a specific employee instance along with other information to those static methods. The disadvantage of doing that is: it's not very object oriented way. If we have multiple specialized types of Employee we would end up creating multiple static methods for each specialized behavior (instead of overriding that behavior) or putting different logic path using instance of in a single static method. That would result in a lot of boilerplate, not very maintainable code. Additionally util methods cannot be discovered by tools and IDEs because they are not bound by any kind of contract on language level.


    synchronized not allowed on default/static methods

    Synchronization is about coordinating shared access to mutable state. It is the class that owns the state that gets to determine that object's synchronization policy not the interfaces. This is the reason synchronized is not deliberately allowed in Java 8 interface default methods, just like other abstract methods


    default/static methods cannot be final

    final default methods are not allowed. The reason is, then the body would not simply be the default implementation, it would be the only implementation. Also in case of ambiguity where we have to override the default method and disambiguate manually, we can't do that if one or both methods are final. The behavior is not definable.


    Only public and private Modifiers allowed on default/static methods

    Since interfaces have always been restricted for having only public members, in Java 8, only public access modifier were allowed on default/static methods.

    Starting Java 9, default/static methods in interfaces can have private modifiers as well.


    Default method cannot override equals/hashCode/toString methods

    All interfaces implicitly override methods from object class. That is the reason we can do something like this without compile time error:

        Runnable r1 = getRunnable();
        Runnable r2 = getRunnable();
        if(r1.equals(r2)){
           ......
        }
    
         r1.clone();
    

    Since all instances of interfaces are Objects, all instances of interfaces have non-default implementations of equals/hashCode/toString already. Therefore, defining a default version of these in an interface is always useless and will cause compile time error.


    Reflection Support

    A new method has been added: java.lang.Method#isDefault() (example)

    See Also