Skip to main content

Java 8 - Default and static methods in Interfaces

Do you know about defaults methods which got added to Java language in JDK 8 release? If yes then that’s very good but if you don’t then you have a good news. In this article we will cover everything you need to know about default methods.
What are default methods?
Let’s take a look at existing stuff first.
As of Java 7, Java interface declares method/s which define a contract. Any class that implements the interface must provide an implementation for each method defined by the interface or inherit the implementation from a superclass.
Note: These methods declared in the interface are implicitly public so you need not mention it in the declaration.
Let’s see an example:
interface Orderable {
    int getProduct( int serialNumber);
    String getProductSpecification();
    float getPrice();
    float getDiscount();
}
Example of class implementing above interface
public class Television implements Orderable {
       // Method implementations
}
Ok so far so good but what happens now if you wish to update your interface. There may be a need to add a method in your existing interface. What effect it will have? 
Yes, you guessed it right. This change will force implementing class to provide implementation for newly added method too in order to reflect the new interface contract or else code won’t compile. This situation is painful especially for library designers as it restricts them from updating the API’s seamlessly without impacting their clients and Java API creators are one of them. Imagine if Java decided to add a method in for. e.g, List interface. This could have broken code of anyone who implemented List interface in their code.  

Now here is something good and new for you, Interfaces in Java 8 can now declare methods with implementation code; yes that’s true. This can happen in two ways.
First, Java 8 allows static methods inside interfaces.
Second, Java 8 introduces a new feature called default methods that allows to provide a default implementation for methods in an interface.
So what this means? It means that if library designer decides to add a method in existing interface then can do so by declaring it as a default method. Existing classes implementing the interface will automatically inherit the default implementations if they don’t provide one explicitly.
Let’s see now how to declare default methods
interface Orderable {
    int getProduct( int serialNumber);
    String getProductSpecification();
    float getPrice();
    float getDiscount();
    default float getPriceWithDiscount(){
    if(getDiscount()==0.0){
       return getPrice();
     }else{
       return (getPrice()-getDiscount());
     }
}
They are declared with the default keyword at the beginning of the method signature, and they provide an implementation. This implementation will be inherited by every class implementing the interface.
Like regular interface methods default methods are also implicitly public hence there’s no need to specify the public modifier.

Benefits of Default methods
Backward compatibility – Default methods allow us to add new methods to an interface that are automatically available in the implementations. Thus, there’s no need to modify the implementing classes. They will inherit the default implementation provided in the interface.
Evolving APIs – As a library designer you can now update your interface and APIs without impacting your clients who are consuming your published API’s. Java 8 creators used it to their advantage and as you will see Java 8 API extensively uses default methods. 
e.g., Now with Java 8 you can call sort() method to sort a list. Earlier we were relying upon utility class Collections sort() method. How? They added a default method in List interface

default void sort(Comparator<? super E> c){
Collections.sort(this, c);
}
Are interfaces like abstract classes now?
Since now interfaces can have default methods which have implementation it seems like there is no difference between an interface and an abstract class. But it’s not true. There is still differences between the two even though default methods have somewhat narrowed gap between them.
So what’s the difference between an abstract class and an interface?
First, a class can extend only from one abstract class, but a class can implement multiple interfaces.
Second, an abstract class can enforce a common state through instance variables (fields) and constructors. An interface can’t have instance variables and they can’t have constructors too.
So, there are indeed differences between the two. Both are used for different purposes and choosing between two really depends on the scenario context.

Multiple Inheritance in Java
Yes, that’s right. Default methods enable something that wasn’t possible earlier: multiple inheritance of behavior. This is the ability of a class to reuse code from multiple places.
As we know a class can implement multiple interfaces and because interface methods can now have implementations in Java 8, classes can inherit behavior (implementation code) from multiple interfaces.
Example: In below example class Z inherits sayHello() from both X and Y.
public interface X {
       default void sayHello() {
              System.out.println("Hello from X");
       }
}

public interface Y extends {
       default void sayHello() {
              System.out.println("Hello from Y");
       }
}

public class Z implements X, Y {
       public static void main(String[] args) {
              new Z().sayHello();
       }
}

This multiple inheritance of behavior can create problems too. If a class implements multiple interfaces and more than one interface defines default method which have same signature then inherited methods will conflict with each other (aka diamond problem). 
Java compiler resolves possible ambiguities when a class inherits several default methods with the same signature.

There are three rules to follow when a class inherits a method with the same signature from multiple places (such as another class or interface):

1. Classes always win. A method declaration in the class or a superclass takes priority over any default method declaration.
E.g., in below example output should be “Hello from Three” since class Three overrides sayHello() and as per the rule classes always win. If Three doesn’t override sayHello() it will be different situation which we will discuss in point 2.

public interface IOne {
       default void sayHello() {
              System.out.println("Hello from IOne");
       }
}

public interface ITwo extends IOne {
       default void sayHello() {
              System.out.println("Hello from ITwo");
       }
}

public class Three implements IOne {
       public void sayHello() {
              System.out.println("Hello from Three");
       }
}

public class Four extends Three implements IOne, ITwo {
       public static void main(String[] args) {
              new Four().sayHello();
       }
}
2. If Rule#1 doesn’t apply then sub-interfaces win: the method with the same signature in the most specific default-providing interface is selected. (If B extends A, B is more specific than A).
E.g., In this case output should be “Hello from ITwo” since ITwo is more specific interface which has default method and Three doesn’t override sayHello so Rule#1 doesn’t apply here.

public interface IOne {
       default void sayHello() {
              System.out.println("Hello from IOne");
       }
}

public interface ITwo extends IOne {
       default void sayHello() {
              System.out.println("Hello from ITwo");
       }
}

public class Three implements IOne {
      
}

public class Four extends Three implements IOne, ITwo {
       public static void main(String[] args) {
              new Four().sayHello();
       }
}
3. Finally, if the choice is still ambiguous, the class inheriting from multiple interfaces has to explicitly select which default method implementation to use by overriding it and calling the desired method explicitly.
E.g., Below code won’t compile because there is an ambiguity. Error message is ‘Duplicate default methods named sayHello with the parameters () and () are inherited from the types ITwo and IOne’

public interface IOne {
       default void sayHello() {
              System.out.println("Hello from IOne");
       }
}

public interface ITwo {
       default void sayHello() {
              System.out.println("Hello from ITwo");
       }
}

public class Three implements IOne, ITwo {
       public static void main(String[] args) {
              new Three().sayHello();
       }
}
To resolve it, Three should override sayHello() and explicitly select which default method implementation to use. With below code output will be “Hello from IOne”

public class Three implements IOne, ITwo {
       public void sayHello() {
              IOne.super.sayHello();
       }     
  public static void main(String[] args) {
              new Three().sayHello();
       }
}

Static Interface Methods
Till now we didn’t stress enough on addition of static methods to Java interfaces. Let’s now try to understand why this is done and how this addition benefits us. Before that, here is an example,
interface Orderable {
    //regular and default methods
    static String getDisclaimer(){
        return “My disclaimer”;
    }
}
How to invoke above method?
static method within an interface is identical to defining one in a class, so we can invoke in exactly same way as we do in case of class static methods. Only difference is in case of interface method name would be preceded by interface name.
Orderable.getDisclaimer();
Why?
A common Java pattern is to define both an interface and a utility companion class defining many static methods for working with instances of the interface. Collections utility class is a good example which is there to work with Collection objects.
Now since with Java 8 we can have static methods exist inside interfaces, such utility classes can be removed and their static methods can be moved inside the interface itself. This will help increase cohesion in our design as all methods are grouped together under interface umbrella and placeholder utility classes could be removed.

Conclusion
That’s it for default methods. In this article we explored default methods, its usage and problem that it solves. Default methods is good addition to Java. It solves major problem which library designers and API creators face. Ideally interfaces should be used just to define the contract but later if situation arises which demands addition of new methods in existing API default methods would come to rescue and allow new methods to be added without compromising on backward compatibility.

Comments

Popular posts from this blog

Local variable type inference : Welcome 'var' in Java

Yes, you read it right. ‘var’ is now available for Java developers as well. JDK 10 which got released on March 20 th , 2018 rolled out a shiny feature which allows developers to use var for declaring local variables. We all know that Java is static typed language. But wait a minute, does this inclusion of var now make Java a dynamic typed language? As we know JavaScript allows to define variables using var and it is indeed dynamically typed language. Quick refresher of static and dynamic types: Static type – Types cannot change at runtime. For example, below java code will not compile int myvar = 1; myvar = “Toyota”; myvar = ["Saab", "Volvo", "BMW"]; Dynamic type – Types can change at run time. For example, below code in JavaScript is valid var myvar; myvar = 1; myvar = “Toyota”; myvar = ["Saab", "Volvo", "BMW"]; So to answer the question “Does this inclusion of var now make Java a dynamic t...