Mar 12, 2015

JAVA 8: Default methods in an interface

Normal interface methods are declared as abstract and must be defined in the class that implements the interface. This 'burdens' the class implementer with the responsibility to implement every declared method. More importantly, this also means that extending an interface is not possible after 'publication'. Otherwise, all implementers would have to adapt their implementation, breaking backwards source and binary compatibility.
To cope with these problems, one of the new features of JDK 8 is the possibility to extend existing interfaces with default methods. Default methods are not only declared, but also defined in the interface. Here is an example:

 public interface Person {  
  public abstract String getFirstName();  
  public abstract String getLastName();  
  public default String getDisplayableFullName() {  
   return getLastName() + ", " + getFirstName();  
  }  
  public abstract boolean isStudent();  
  public default boolean isNotStudent() {  
   return !isStudent();  
  }  
 }  


Implementers of the Person interface need to define getFirstName(), getLastName(), and isStudent(). But they can choose not to implement getDisplayableFullName(), or isNotStudent(). Implementers can still override default methods, like regular non-final class methods can be overridden in subclasses. Abstract classes can even (re)declare default methods as abstract, forcing subclasses to reimplement the method (sometimes called 're-abstraction').

Use cases for default methods

Extending existing interfaces

The most important use case for this new feature in the JDK libraries is the possibility to extend existing interfaces without breaking existing implementers: adding a new abstract method to an interface would require all implementing classes to implement that new method. And extending existing interfaces was necessary to allow for the backwards-compatible implementation of the new Stream API that will extend the Collections API (see my third blog post on JDK 8). A snippet from the new Collection interface:

 public interface Collection<E> extends Iterable<E> {  
  // Traditional, abstract methods:  
  int size();  
  boolean contains(Object o);  
  boolean add(E e);  
  boolean remove(Object o);  
  // Etc...  
  // New methods stream() and parallelStream() with default implementations:  
  default Stream<E> stream() {  
   return Streams.stream(() -> Streams.spliterator(iterator(), size(), Spliterator.SIZED), Spliterator.SIZED);  
  }  
  default Stream<E> parallelStream() {  
   return stream().parallel();  
  }  
 }  

The Collection interface is extended with the stream() and parallelStream() methods. Each must be defined as default method, otherwise existing Collection implementers (such as ArrayList, or from third parties) would break, or would have to implement those methods.

Additional design flexibility in the inheritance hierarchy

Sometimes functionality can be shared between different implementing classes. Traditionally, this was done by creating an (abstract) superclass for the different classes. In other cases, such as when defining a listener for an event, an interface with multiple methods must be implemented, but the implementer is only interested in implementing a single event, i.e., a single method. Usually an abstract (adapter) class is extended to provide default implementations for these interface methods.
In both situations, default method implementations can provide more design flexibility. Similar to traits in Scala, default methods enable the shari
ng or reuse of behavior between different types. And because a class can implement multiple interfaces, a safe and limited form of multiple inheritance is supported. To share state, an (abstract) superclass is still required.

Alternative: extension methods

As an alternative, a variant of C# extension methods was considered. However, extension methods introduce new 'static-ness': they are static methods that take as their first argument the object on which they operate. They are basically syntactic sugar: the construct allows the programmer to 'dot into' the extension method in an IDE.
One of the problems with this approach is that implementers could not override the method, and the entire class hierarchy would be stuck with a one-size-fits-all implementation. In general, introducing additional static methods on a large scale in this manner was considered undesirable. Also, extension methods would not behave nicely when using reflection: the extension methods would not be discoverable, as they would not be part of the declared methods of a class (consider the situation in which the mail class is loaded, inspected through reflection, and only then the class with extension methods is loaded - the JVM would never know about the extension methods).


Static methods can also be written in interfaces in Java 8. Read more about interface static methods here.
You can also follow us at +Java Territory to stay updated with latest articles.

0 comments:

Post a Comment