Another new addition to the Java repertoire are default methods for interfaces: Write a non-abstract method directly in the interface instead of its implementation. We can still override it, but we don’t have too.

Problem: backward compatibility

Imagine the following (simplified) interface for a list:

1
2
3
4
5
public interface List {  
    void add(Object obj);  
    Object get(int index);  
    boolean isEmpty();  
}

In a typical project we might implement several list types with this interface, and now we have a great idea: more functionality!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public interface List {  

    // Old methods  
    void add(Object obj);  
    Object get(int index);  
    boolean isEmpty();

    // New methods  
    Object first();  
    void sort();  
}

An addition to the interface would mean we just broke the contract the interface declares, resulting in incompatibility with every class that implemented the interface. This means a lot of work to fix it.

The incompatibility issue is even more relevant if someone other than us is relying on our interface in their projects. But with default methods, we can avoid these issues!

Solution: default methods

Instead of implementing the two new methods on every implementation of List, we provide an implementation directly on the interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public interface List {  

    // Old methods  
    void add(Object obj);  
    Object get(int index);  
    boolean isEmpty();

    // New methods  
    default Object first() {  
        if (isEmpty()) {  
            return null;  
        }  
        return get(0);  
    }

    default void sort() {  
        throw new UnsupportedOperationException();  
    }  
}

This example shows the two ways you could and should use default methods for backward compatibility:

  • Implementing new functionality that is common for most of the classes: first()
  • Throwing an exception to indicate you NEED to implement a certain feature yourself: sort()

Conclusion

Default methods are a great way to ensure backward compatibility in our projects, and might even save us writing the same code over and over again. We only have access to the interface methods, so we can’t implement much functionality. But often, like in our example, it’s enough.