Java 8 Default Methods On Interface : What, Why and Example

Whenever someone talks about Java 8, the first thing he speaks about is lambda expression and How lambda expression will change the way you use Collections API today. In truth, lambda expression would not be that useful had language not been enhanced to support default methods on Java Interface. Also known as a virtual extension or defender methods, they allow you to declare a non-abstract method inside Java interface. Which means, finally you can add new methods without breaking all classes, which implements a certain interface. This opens a new path for enhancing and evolving existing Collection API to take advantage of lambda expressions. For example, now you can iterate over all elements of Collection in just one line, as opposed to four lines it requires you to do prior to Java 8.

The forEach is a default method declared inside java.lang.Iterable, which allows Java to iterate over collection internally and execute action asked by the client. Since now iteration is internally handled by Java, they can do a lot of things which were not possible earlier e.g. parallel iteration.

Parallel libraries are long been due on Java platform to leverage immense power of multiple CPUs available to modern servers. Since handling parallelism on the client side is both difficult and error prone, programmers have been asking for libraries, which can do things in parallel, leaving you to only define what to do, instead of how to do.

Bulk data operation, e.g. applying logic on each or some elements of a Collection was possible because of default methods like forEach.

In this article, we will learn what is default method, will see few examples of default method on the custom interface and from JDK itself, understand how differences between abstract class and the interface in Java 8 are reduced due to the introduction of defender methods and finally answer some frequently asked questions on Java 8 default methods. So fasten your seat belt and be ready for a short ride on Java 8 default methods.



What is Default or Defender Methods of Java 8

Default methods, also known as virtual extension methods or defender methods is a non-abstract method, which can be declared inside interface in Java. If you have been using interface in Java then you know that it’s not possible to change the structure of the interface, without breaking its implementations, once it's published, i.e. you cannot add new methods on existing interface.

This was a serious limitation and was affecting the fluent use of lambda expression with Collections API. Since one of the common use cases of a lambda expression is to apply some operation to each element of Collection, it is not possible until you have a method, which allows you to iterate over List. Adding a method like forEach() inside java.util.List would have broken all List implementation exists in Java world, and that's why the idea of default method was introduced.

This way, you can declare a concrete method inside interface without breaking its clients. Since introducing this means, an interface is no more completely abstract, a few rules in the compiler is needed to ensure that it wouldn't create the diamond problem, one of the reasons Java doesn't support multiple inheritances. See Java SE 8 for Really Impatient to learn more about it.

Reserve keyword default is used to make a method default inside interface, it’s not mandatory for an implementer to override Java 8 default methods, but it can if it wish to. Also, no special bytecode instruction is provided for calling default methods, It will make use of existing invokeinterface, invokevirtual, and invokespecial bytecode method instructions.



Why Default Methods was Introduced in Java 8

The lambda expression in Java is of SAM type, which means a type of Single abstract method, interface like Comparable, Comparator and Runnable fits this bill and were an obvious candidate to be used in a lambda expression, but another use case of using lambdas is allowing to operate them over each element of Collection.

Since so far collection doesn't know how to iterate over themselves, adding a method like forEach() on Collection or List interface would have broken all existing implementation, so they thought about a Virtual extension or defender methods. Which means you can finally declare a non-abstract method inside interface, without breaking its client.

Had they introduced Java 8 lambda expression without default method like forEach() on java.lang.Iterable, It wouldn't help in the development of parallel libraries, which is core intention of introducing lambda expression to exploit the capability of multiple CPUs available in modern computers. So in a sense, default method was critical to support the development of bulk data operations with a lambda expression.



Default Method Example in Java 8

Here is a simple example of an interface, which has implemented Java 8 default methods. You need to download JDK 8 to run this program.

There are a lot of interesting things to note about this example, first of all, did you notice that class TextParser neither implements parse() method not declared as abstract, but it still compiles fine. This is possible because parse() is a default method, had Parser contains another method, which was not a defender method, TextParser either has to override that method or declare itself as abstract.

Next interesting thing is what happens if parse() method is called with an instance of TextParser class? Well, it will call default implementation provided in the interface itself. Next is our class XMLParser, which does override parse(), which means it’s possible to override default methods in Java.

Now if you invoke default method with an instance of subclass e.g. XMLParser, it will invoke overridden a method, as shown in the output.

interface Parser{   
    default void parse(){
        System.out.println("default Parsing logic");
    }
}

class TextParser implements Parser{ 
    // No compile time error, because parse is default method
    //inherit default implementation of parse
}

public class XMLParser implements Parser{
   
    @Override
    public void parse(){
        System.out.println("Parsing XML files");
    }
   
    public static void main(String args[]){
        Parser p = new TextParser();
        p.parse();
       
        p = new XMLParser();
        p.parse();
    }
}

Output:
default Parsing logic
Parsing XML files


Let's take another real example of default methods, which is probably the most popular one, which connects default methods to lambda expressions. The forEach method is defined as default method inside java.lang.Iterable class as shown below :

public interface Iterable<T> {
    Iterator<T> iterator();
 
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

Since List implements Collection interface, which in turn implements Iterable, this allows you to write following kind of code, which operates on each element.  This code print each element from a list :

List<Integer> numbers = Arrays.asList(1,2,3,4,5,6);
numbers.forEach(System.out::println);

The forEach() is just an example. The JDK designers have added several useful methods in existing interfaces e.g. sort() in List and removeIf() in Collection. There are a couple in the Map interface as well. You should check a good Java book e.g. Java 8 in Action to find out more about such important methods.
Java 8 Default Methods On Interface


That's all about what is default methods in Java 8, Why default or defender methods were added in Java programming language and how default methods works. Though they are instrumental in the evolution of lambda expression and Collections API in Java 8, they are not something, which should be used every day.

Treat your interface as pure interface and only add default methods, if it becomes really necessary, keep it as your last option, not the first one, similar to how they have been used in Java 8. Remember Java 8 is not supporting multiple inheritances of classes, and even with default method, the compiler will ensure it won't cause ambiguity to avoid the diamond problem.

To take full advantage of lambda expression and default methods, take a look at Java 8 API docs to see which interfaces are enhanced with default methods and How you can use them with a lambda expression.

Further Reading
What's New in Java 8
Java SE 8 for Really Impatient
From Collections to Streams in Java 8 Using Lambda Expressions


Related Java 8 Tutorials
If you are interested in learning more about new features of Java 8, here are my earlier articles covering some of the important concepts of Java 8:
  • 5 Books to Learn Java 8 from Scratch (books)
  • How to join String in Java 8 (example)
  • How to use filter() method in Java 8 (tutorial)
  • How to format/parse the date with LocalDateTime in Java 8? (tutorial)
  • How to use Stream class in Java 8 (tutorial)
  • How to convert List to Map in Java 8 (solution)
  • 20 Examples of Date and Time in Java 8 (tutorial)
  • How to use peek() method in Java 8 (example)
  • How to sort the map by keys in Java 8? (example)
  • How to sort the may by values in Java 8? (example)
  • 10 examples of Optionals in Java 8? (example)

Thanks for reading this article so far. If you like this article then please share with your friends and colleagues. If you have any question or feedback then please drop a comment.

1 comment:

  1. Good content. It could be improved by adding on how Java8 provides solution to "diamond problem". i.e. FileParser.super.parse() example

    ReplyDelete