Default and static methods in interfaces in Java 8

Before Java 8 interfaces could only contain static fields (usually simple constants) and abstract methods. Java 8 provided the ability to define concrete (default and static) methods in interfaces. This new language feature is used extensively in Java core packages.

Static methods

Static method in interface looks the same like in a normal class:

public interface Checker {
    ...
    public static boolean isNull(Object obj) {
        return obj == null;
    }
    ...

The main reason to add static methods to interfaces is to keep related utility methods in one place so that they can be easily used by subclasses, default methods in subinterfaces or by users of this interface.

Default methods

Default method looks like a typical class method but is defined inside an interface and contains default specifier. Let’s look at Collection.removeIf() default method:

default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

Default method can access everything that is defined within this interface or is inherited by this interface, including:

  • reference to this
  • all abstract methods defined in this or super-interfaces
  • all default or static methods defined in this or super-interfaces
  • all static fields defined in this or super-interfaces

Default methods allow adding new functionality to existing interfaces without breaking all existing implementations – they preserve backwards compatibility. A class, that implements an interface with a default method, gets the default implementation from the interface but it can still override the default implementation.

Default and static methods in functional interfaces

The functional interface can contain multiple default and static methods and still be functional. In fact, default and static methods are not abstract and are not counted within the limit of exactly one abstract method. Here is an example:

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    ....
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }
    public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(true, comparator);
    }
    ....

Extending interfaces which contain default methods

If we create a new interface which extends an interface which contains a default method, we have 3 possibilities:

  • Not mention the default method in the new interface. This way the new interface will inherit the default method from parent.
  • Override the default method by redefining it in the new interface and providing new method body. All subclasses and subinterfaces will use new definition of the default method.
  • Declare the default method as abstract in the new interface. This way the default method must be overridden in subclasses or subinterfaces of the new interface.

Default method ambiguity

Sometimes we may want to implement two interfaces which contain default methods with the same method signature (name, parameters, and so on):

public interface InterfaceOne { 
    default void doSomething() { 
        ...
    } 
}
public interface InterfaceTwo {
    default void doSomething() {
        ...
    }
}
public class MyClass implements InterfaceOne, InterfaceTwo  {
}

In this rare case the compilation will fail because Java compiler does not know which implementation of the default method it should choose for the class. To resolve this issue we have to explicitly redefine/redeclare the default method in the class. We have two possibilities here.

The first one is to simply override the default method in the class and provide new method body:

public class MyClass implements InterfaceOne, InterfaceTwo  {
    public void doSomething() {
        // some code
        InterfaceOne.super.doSomething();
    }
}

Please, note that we are not using default keyword anymore. We can also use following syntax:

InterfaceOne.super.doSomething();

to call default implementation from one of the implemented interfaces.

Alternatively, we can declare the default method in the class as abstract:

public abstract class MyClass implements InterfaceOne, InterfaceTwo  {
        public abstract void doSomething();
    }

As a result the class must be made abstract also. This way we can somehow “postpone” the problem because the concrete subclass will have to redefine this default method.

Conclusion

Many static and default methods have been added to existing interfaces since Java 8 to simplify their usage and promote code reuse. Some of these interfaces include: Iterator, Iterable, Comparator, Collection.

Advertisements

About Robert Piasecki

Husband and father, Java software developer, Linux and open-source fan.
This entry was posted in Java and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s