Technology

Introduction to Default Methods in Java 8 with Examples

This is a guide to Java 8 Default Methods. You will learn why Java created Default methods, benefits and how to use them with the help of examples.

What is Default Method ?

The Default Method is a feature of Interfaces in Java 8. Therefore, since Java 8 the interfaces are allowed to have concrete implementation of methods – declared as default.

Traditionally, Interfaces only used to have method declarations (not implementations). This was one of the major differences between Java Abstract Class and Java Interfaces. However, adding capability of Default Methods in interfaces, doesn’t really makes them similar. Because, there are fundamental differences between Abstract Class and Interfaces.

Inheriting Default Methods

public interface TestInterface {

    //Abstract
    void oldMethod();

    // Default
    default void latestMethod() {
        System.out.println("This is new Method");
    }
}

Above is an example of Java 8 Default Methods. Where, the method testMethod is a traditional method declaration. However the defaultMethod has a body and default modifier. This modifier is mandatory if you want to put a concrete method in an interface (java 8 and above).

Now, let’s see the interface implementation.

public class LegacyImplementation implements TestInterface {

    @Override
    public void oldMethod() {
        System.out.println("Old implementation");
    }


    public static void main(String[] ar) {
        TestInterface test = new LegacyImplementation();
        test.oldMethod();
        test.latestMethod();
    }
}

The Legacy implementation doesn’t have to implement the newly added default method. When we run the class:

Old implementation
This is new Method

Overriding Default Methods

A class can overrides a super class method. Similarly, the a class can also override (re-implement) a default method of its interface.

Let’s change the LegacyImplementation we have seen earlier. This time, it implements both of the methods from its interface.

public class LegacyImplementation implements TestInterface {

    @Override
    public void oldMethod() {
        System.out.println("Old implementation");
    }

    @Override
    public void latestMethod(){
        System.out.println("This time I am implemented");
    }


    public static void main(String[] ar) {
        TestInterface test = new LegacyImplementation();
        test.oldMethod();
        test.latestMethod();
    }
}

finally the output – as expected.

Old implementation
This time I am implemented

Why Default Methods ?

The Default Methods in Java provides a feature of having concrete methods in interfaces. However, it is not an improvement, but a rectification of old mistakes Java did long ago.

Long ago, when the developers at Java wrote some of the core APIs, they used Interfaces to define API interfaces. For example, Java Collections API. Hence, most of the interfaces in collections API are actually Java Interfaces.

But, with the introduction of Java 8 Streams, Java wanted to modify collections API to provide support for streams. However, if you add new methods in a Java Interface, the implementations need to implement them. Moreover, Java tries to provide excellent backward compatibility. In other words, previously written implementations of List or Map should be supported during version upgrades.

Hence, to solve this problem, Java brought in the concept of having concrete methods in Interfaces. Because of which, the implementations decides whether to implement a method or use the default implementation.

Static Methods in Interfaces

It lets you to define fully implemented static methods in Interfaces. Because of which, you can have a bunch of Utility methods defined in interfaces. Many a times we need a class having bunch of static utility methods. However, having static methods is interfaces, now, you can use interfaces instead.

public interface TestInterface {
    static int add(int x, int y) {
        return x + y
    }

    static int product(int x, int y) {
        return x * y;
    }
}

These type of Utility interfaces are really useful.

public static void main(String[] ar) {
    TestInterface.add(10, 20);
    TestInterface.product(5, 4);
}

Multiple Inheritance

Although, Java doesn’t support multiple inheritance it does support implementing multiple interfaces. Since, before Java 8, interfaces never had concrete methods, they never caused a problem of multiple inheritance. But, having concrete methods interfaces Java 8 onwards, the multiple inheritance behaviour is worth understanding.

Firstly, Inheritance is an ability of a class by which, it can inherit method definitions from multiple classes. However, multiple inheritance issue arrises when two super classes or super interfaces have methods with exactly same signature. Because, it produces ambiguity. Java doesn’t support extending multiple classes and hence the problem is now limited to interfaces with default methods. Let’s see how Java responds to this issue.

interface InterfaceA {
    default void doSomthing() {
        System.out.println("doSomthing in InterfaceA");
    }
}

interface InterfaceB extends InterfaceA {
    default void doSomthing() {
        System.out.println("doSomthing in InterfaceB");
    }
}

class ClassC implements InterfaceB {}

Both interfaces have methods of same name. Also, InterfaceB extends InterfaceA.

ClassC c = new ClassC();
c.doSomthing();

// Output:
// doSomthing in InterfaceA

When we call the method on ClassC instance the immediate super interface’s method is called.

Now, we will see one more scenario. Here, ClassC implements both InterfaceA and InterfaceB.

interface InterfaceA {
    default void doSomthing() {
        System.out.println("doSomthing in InterfaceA");
    }
}

interface InterfaceB {
    default void doSomthing() {
        System.out.println("doSomthing in InterfaceB");
    }
}

//Compilation Error :-(
class ClassC implements InterfaceA, InterfaceB {}

This code doesn’t compile because there is an ambiguity of method names. Java compels ClassC to put implementation for the ambiguous method. However, the ClassC can still delegate call to super interface method of its choice. Let’s see how.

class ClassC implements InterfaceA, InterfaceB {

    @Override
    public void doSomthing() {
        // delegation to the super interface method of choice
        InterfaceB.super.doSomthing();
    }
}
ClassC c = new ClassC();
c.doSomthing();


// Output
// doSomthing in InterfaceB
    

You can see the method of InterfaceB .was called.

Summary

Since Java 8 the Interfaces can have concrete methods and those methods are called as Default Methods. Java introduced the default methods to provide backward compatibility to the legacy implementations of Java interfaces. Also the Interfaces can have static methods with full implementation.

Moreover, Java avoids problem of Multiple Inheritance by compelling the implementing class to provide an implementation of ambiguous methods.