Default and Static Methods in Java Interfaces

While lambdas are the most prominent addition to Java 8, there are many other new features, such as functional interfaces, virtual methods, class and method references, new time and date API, JavaScript support, default and static methods in interfaces, and so on. In this post, I will focus on default and static methods in interfaces, as understanding this feature is a must for any Java programmer on-boarding to Java 8 onward.

Background

Let refresh our understanding about Interfaces in Java.

Before Java 8, interfaces could have only abstract methods. Classes that implement an interface, must implements all it's abstract method. Can you imagine that if the interface is changed, and a new method is added. Then we must change all classes to implements the new abstract method. To overcome this issue, Java 8 has introduced the concept of default methods which allow the interfaces to have methods with implementation without affecting the classes that implement the interface.

Default Methods


As example, we have interface Calculator, which is implemented in SimpleCalculator class.

Calculator.java
public interface Calculator {

    double add(double n1, double n2);
    double subtract(double n1, double n2);
    double multiply(double n1, double n2);
    double divide(double n1, double n2);
}
                    

SimpleCalculator.java
public class SimpleCalculator implements Calculator {

    @Override
    public double add(double n1, double n2) {
        return n1 + n2;
    }

    @Override
    public double subtract(double n1, double n2) {
        return n1 - n2;
    }

    @Override
    public double multiply(double n1, double n2) {
        return n1 * n2;
    }

    @Override
    public double divide(double n1, double n2) {
        return n1 / n2;
    }    
}
                    

Everything looks OK, until the programmer decided to add one abstract method in Calculator interface.

Calculator.java
public interface Calculator {

    double add(double n1, double n2);
    double subtract(double n1, double n2);
    double multiply(double n1, double n2);
    double divide(double n1, double n2);
    double power(double n1, double n2);
}
                    

Now, class SimpleCalculator need to implements method power(..) to avoid compilation error. Here how default method in Java 8 interfaces saved the day. We just need to change our interface Calculator code into:

Calculator.java
public interface Calculator {

    double add(double n1, double n2);
    double subtract(double n1, double n2);
    double multiply(double n1, double n2);
    double divide(double n1, double n2);
    
    default double power(double n1, double n2) {
        double p = n1;

        for (int x=2; x<=n2; x++){
            p *= n1;
        }

        return p;
    }
}
                    

And when we run a CalculatorApp:

CalculatorApp.java
public class CalculatorApp {
    
    public static void main(String[] args) {
        
        Calculator calc = new SimpleCalculator();
        double n1 = 18;
        double n2 = 6;
        
        System.out.println(calc.add(n1, n2));
        System.out.println(calc.subtract(n1, n2));
        System.out.println(calc.multiply(n1, n2));
        System.out.println(calc.divide(n1, n2));
        System.out.println(calc.power(n1, n2));
    }
}

/*
Output:
------    
24.0
12.0
108.0
3.0
46656.0
*/
                    

Please notice how the default methods power(..) from Calculator interface are automatically available in the SimpleCalculator class, and invokable in object calc which is an instance of SimpleCalculator.

If in the future, the programmer decide to add more and more default methods to the Calculator interface, the application will still continue working, and the subclass is not forced to provide implementations for the new methods.

Multiple Interface Inheritance

Now we have another interface, Maths.

Maths.java
public interface Maths {

    double add(double n1, double n2);
    double subtract(double n1, double n2);
    double multiply(double n1, double n2);
    double divide(double n1, double n2);
    
    default double power(double n1, double n2) {
        return Math.pow(n2, n2);
    }
}
                    

Then our class SimpleCalculator also implements Maths

public class SimpleCalculator 
    implements Calculator, Maths {

    // methods implementation
       
}
                    

In this case, we will get compilation error:

SimpleCalculator inherits unrelated defaults for power(double,double) from types Calculator and Maths

There’s a conflict caused by multiple interface inheritance since SimpleCalculator class would inherit both default methods of Calculator and Maths. To solve this ambiguity, SimpleCalculator must explicitly provide an implementation for power(double,double) method.

SimpleCalculator.java
public class SimpleCalculator 
    implements Calculator, Maths {

    @Override
    public double add(double n1, double n2) {
        return n1 + n2;
    }

    @Override
    public double subtract(double n1, double n2) {
        return n1 - n2;
    }

    @Override
    public double multiply(double n1, double n2) {
        return n1 * n2;
    }

    @Override
    public double divide(double n1, double n2) {
        return n1 / n2;
    }
    
    @Override
    public double power(double n1, double n2) {
        return Math.pow(n2, n2);
    }
}
                    

Static Methods

A static method is a method that is associated with the class in which it is defined rather than with any object. In addition to default methods, Java 8 now enable you to define static methods in interfaces (which is previously is only available in classes). Now, programmer can keep static methods specific to an interface in the same interface rather than in a separate class. For sure this is makes it easier for programmer to organize helper methods in your libraries.

public interface Calculator {

    // abstract methods
    // default methods
    
    static String toBinaryString(double n) {
        return Integer.toBinaryString(new Double(n).intValue());
    }
    
    static String toHexString(double n) {
        return Integer.toHexString(new Double(n).intValue());
    }

    static String toOctalString(double n) {
        return Integer.toOctalString(new Double(n).intValue());
    }
}
                    

To call static methods, we just call the method from Calculator:

CalculatorApp.java
public class CalculatorApp {
    
    public static void main(String[] args) {
        
        double n = 108.36;
        System.out.println(Calculator.toBinaryString(n));
        System.out.println(Calculator.toHexString(n));
        System.out.println(Calculator.toOctalString(n));
    }
}

/*
Output:
------    
1101100
6c
154
*/
                    

In Summary

  • Use default methods in interfaces to incrementally enhance functionality to a given type without breaking down the implementing classes. This is a very good approach for backward compability
  • Use static methods in interfaces to provide utility methods, without having additional utility classes (that normally used to group static methods).