111

In Java 8, interfaces can contain implemented methods, static methods, and the so-called "default" methods (which the implementing classes do not need to override).

In my (probably naive) view, there was no need to violate interfaces like this. Interfaces have always been a contract you must fulfill, and this is a very simple and pure concept. Now it is a mix of several things. In my opinion:

  1. static methods do not belong to interfaces. They belong to utility classes.
  2. "default" methods shouldn't have been allowed in interfaces at all. You could always use an abstract class for this purpose.

In short:

Before Java 8:

  • You could use abstract and regular classes to provide static and default methods. The role of interfaces is clear.
  • All the methods in an interface should be overriden by implementing classes.
  • You can't add a new method in an interface without modifying all the implementations, but this is actually a good thing.

After Java 8:

  • There's virtually no difference between an interface and an abstract class (other than multiple inheritance). In fact you can emulate a regular class with an interface.
  • When programming the implementations, programmers may forget to override the default methods.
  • There is a compilation error if a class tries to implement two or more interfaces having a default method with the same signature.
  • By adding a default method to an interface, every implementing class automatically inherits this behavior. Some of these classes might have not been designed with that new functionality in mind, and this can cause problems. For instance, if someone adds a new default method default void foo() to an interface Ix, then the class Cx implementing Ix and having a private foo method with the same signature does not compile.

What are the main reasons for such major changes, and what new benefits (if any) do they add?

8
  • 31
    Bonus question: Why didn't they introduce multiple inheritance for classes instead?
    – user80577
    Commented Mar 21, 2014 at 9:34
  • 2
    static methods do not belong to interfaces. They belong to utility classes. No they belong in the @Deprecated category! static methods are one of the most abused constructs in Java, because of ignorance and laziness. Lots of static methods usually means incompetent programmer, increase coupling by several orders of magnitude and are a nightmare to unit test and refactor when you do realize why they are a bad idea!
    – user7519
    Commented Sep 4, 2014 at 2:02
  • 12
    @JarrodRoberson Can you provide some more hints (links would be great) about "are a nightmare to unit test and refactor when you do realize why they are a bad idea!"? I never thought that and I'd like to know more about it.
    – watery
    Commented Sep 4, 2014 at 6:16
  • 11
    @Chris Multiple inheritance of state causes a bunch of problems, especially with memory allocation (the classic diamond problem). Multiple inheritance of behavior, however, only depends on the implementation meeting the contract already set by the interface (the interface can call other methods it declares and requires). A subtle, but very interesting, distinction.
    – ssube
    Commented Oct 1, 2014 at 17:29
  • 1
    you want to add method to existing interface, was cumbersome or almost not possible before java8 now u can just add it as default method.
    – VedantK
    Commented Aug 27, 2015 at 8:44

5 Answers 5

63

A good motivating example for default methods is in the Java standard library, where you now have

list.sort(ordering);

instead of

Collections.sort(list, ordering);

I don't think they could have done that otherwise without more than one identical implementation of List.sort.

16
  • 19
    C# overcomes this problem with Extension Methods. Commented Mar 20, 2014 at 15:50
  • 5
    and it allows a linked list to use a O(1) extra space and O(n log n) time mergesort, because linked lists can be merged in place, in java 7 it dumps to an external array and then sort that Commented Mar 20, 2014 at 16:32
  • 5
    I've found this paper where Goetz explains the problem. So i'll be marking this answer as the solution for now. Commented Mar 21, 2014 at 10:01
  • 1
    @RobertHarvey: Create two hundred-million-item List<Byte>, use IEnumerable<Byte>.Append to join them, and then call Count, then tell me how extension methods solve the problem. If CountIsKnown and Count were members of IEnumerable<T>, the return from Append could advertise CountIsKnown if the constituent collections did, but without such methods that's not possible.
    – supercat
    Commented Jun 12, 2014 at 22:30
  • 6
    @supercat: I haven't the slightest idea what you are talking about. Commented Jun 12, 2014 at 22:35
56

The correct answer is in fact found in the Java Documentation, which states:

[d]efault methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.

This has been a long-standing source of pain in Java, because interfaces tended to be impossible to evolve once they were made public. (The content in the documentation is related to the paper that you linked to in a comment: Interface evolution via virtual extension methods.) Furthermore, rapid adoption of new features (e.g. lambdas and the new stream APIs) can only be done by extending the existing collections interfaces and providing default implementations. Breaking binary compatibility or introducing new APIs would mean that several years would pass before Java 8's most important features would be in common use.

The reason for allowing static methods in interfaces is again revealed by the documentation: [t]his makes it easier for you to organize helper methods in your libraries; you can keep static methods specific to an interface in the same interface rather than in a separate class. In other words, static utility classes like java.util.Collections can now (finally) be considered an anti-pattern, in-general (of course not always). My guess is that adding support for this behavior was trivial once virtual extension methods were implemented, otherwise it probably wouldn't have been done.

On a similar note, an example of how these new features can be of benefit is to consider one class that has recently annoyed me, java.util.UUID. It doesn't really provide support for UUID types 1, 2, or 5, and it cannot be readily modified to do so. It's also stuck with a pre-defined random generator that cannot be overridden. Implementing code for the unsupported UUID types requires either a direct dependency on a third-party API rather than an interface, or else the maintenance of conversion code and the cost of additional garbage collection to go with it. With static methods, UUID could have been defined as an interface instead, allowing real third-party implementations of the missing pieces. (If UUID were originally defined as an interface, we'd probably have some sort of clunky UuidUtil class with static methods, which would be awful too.) Lots of Java's core APIs are degraded by failing to base themselves on interfaces, but as of Java 8 the number of excuses for this bad behavior have thankfully diminished.

It's not correct to say that [t]here's virtually no difference between an interface and an abstract class, because abstract classes can have state (that is, declare fields) while interfaces cannot. It is therefore not equivalent to multiple inheritance or even mixin-style inheritance. Proper mixins (such as Groovy 2.3's traits) have access to state. (Groovy also supports static extension methods.)

It's also not a good idea to follow Doval's example, in my opinion. An interface is supposed to define a contract, but it is not supposed to enforce the contract. (Not in Java anyway.) Proper verification of an implementation is the responsibility of a test suite or other tool. Defining contracts could be done with annotations, and OVal is a good example, but I don't know whether it supports constraints defined on interfaces. Such a system is feasible, even if one does not currently exist. (Strategies include compile-time customization of javac via the annotation processor API and run-time bytecode generation.) Ideally, contracts would be enforced at compile-time, and worst-case using a test suite, but my understanding is that runtime enforcement is frowned upon. Another interesting tool that might assist contract programming in Java is the Checker Framework.

8
  • 1
    For further follow-up on my last paragraph (i.e. don't enforce contracts in interfaces), it's worth pointing out that default methods cannot override equals, hashCode and toString. A very informative cost/benefit analysis of why this is disallowed can be found here: mail.openjdk.java.net/pipermail/lambda-dev/2013-March/…
    – ngreen
    Commented Oct 6, 2014 at 17:13
  • It's too bad that Java only has a single virtual equals and a single hashCode method since there are two different kinds of equality which collections might need to test, and items which would implement multiple interfaces may be stuck with conflicting contractual requirements. Being able to use lists that aren't going to change as hashMap keys is helpful, but there are also times when it would be helpful to store collections in a hashMap which matched things based upon equivalence rather than current state [equivalence implies matching state and immutability].
    – supercat
    Commented Oct 11, 2014 at 17:12
  • Well, Java sort of has a workaround with the Comparator and Comparable interfaces. But those are kind of ugly, I think.
    – ngreen
    Commented Oct 14, 2014 at 2:50
  • Those interfaces are only supported for certain collection types, and they pose their own problem: a comparator may itself potentially encapsulate state (e.g. a specialized string comparator might ignore a configurable number of characters at the start of each string, in which case the number of characters to ignore would be part of the comparator's state) which would in turn become part of the state of any collection which was sorted by it, but there's no defined mechanism for asking two comparators if they are equivalent.
    – supercat
    Commented Oct 14, 2014 at 2:56
  • Oh yes, I get the pain of comparators. I'm working on a tree structure that ought to be simple but isn't because it's so hard to get the comparator right. I'm probably going to write a custom tree class just to make the problem go away.
    – ngreen
    Commented Oct 14, 2014 at 3:00
45

Because you can only inherit one class. If you've got two interfaces whose implementations are complex enough that you need an abstract base class, those two interfaces are mutually exclusive in practice.

The alternative is to convert those abstract base classes into a collection of static methods and turn all the fields into arguments. That would allow any implementor of the interface to call the static methods and get the functionality, but it's an awful lot of boilerplate in a language that's already way too verbose.


As a motivating example of why being able to provide implementations in interfaces can be useful, consider this Stack interface:

public interface Stack<T> {
    boolean isEmpty();

    T pop() throws EmptyException;
 }

There's no way to guarantee that when someone implements the interface, pop will throw an exception if the stack is empty. We could enforce this rule by separating pop into two methods: a public final method that enforces the contract and a protected abstract method that performs the actual popping.

public abstract class Stack<T> {
    public abstract boolean isEmpty();

    protected abstract T pop_implementation();

    public final T pop() throws EmptyException {
        if (isEmpty()) {
            throw new EmptyException();
        else {
            return pop_implementation();
        }
    }
 }

Not only do we ensure all implementations respect the contract, we've also freed them from having to check if the stack is empty and throwing the exception. It's a big win!...except for the fact that we had to change the interface into an abstract class. In a language with single inheritance, that's a big loss of flexibility. It makes your would-be interfaces mutually exclusive. Being able to provide implementations that only rely on the interface methods themselves would solve the problem.

I'm not sure whether Java 8's approach to adding methods to interfaces allows adding final methods or protected abstract methods, but I know the D language allows it and provides native support for Design by Contract. There is no danger in this technique since pop is final, so no implementing class can override it.

As for default implementations of overridable methods, I assume any default implementations added to the Java APIs only rely on the contract of the interface they were added to, so any class that correctly implements the interface will also behave correctly with the default implementations.

Moreover,

There's virtually no difference between an interface and an abstract class (other than multiple inheritance). In fact you can emulate a regular class with an interface.

This is not quite true since you can't declare fields in an interface. Any method you write in an interface can't rely on any implementation details.


As an example in favor of static methods in interfaces, consider utility classes like Collections in the Java API. That class only exists because those static methods can't be declared in their respective interfaces. Collections.unmodifiableList could've just as well been declared in the List interface, and it would've been easier to find.

15
  • 4
    Counter-argument: since static methods, if written properly, are self-contained, they make more sense in a separate static class where they can be collected and categorized by class name, and less sense in an interface, where they are essentially a convenience that invites abuses like holding static state in the object or causing side effects, making the static methods untestable. Commented Mar 20, 2014 at 16:23
  • 3
    @RobertHarvey What's to stop you from doing equally silly things if your static method is in a class though? Also, that the method in the interface may not require any state at all. You might simply be trying to enforce a contract. Suppose you've got a Stack interface and you want to ensure that when pop is called with an empty stack, an exception is thrown. Given abstract methods boolean isEmpty() and protected T pop_impl(), you could implement final T pop() { isEmpty()) throw PopException(); else return pop_impl(); } This enforces the contract on ALL implementors.
    – Doval
    Commented Mar 20, 2014 at 16:44
  • Wait, what? Push and Pop methods on a stack are not going to be static. Commented Mar 20, 2014 at 16:46
  • @RobertHarvey I would've been clearer if not for the character limit on comments, but I was making the case for default implementations in an interface, not static methods.
    – Doval
    Commented Mar 20, 2014 at 16:47
  • 8
    I think default interface methods are rather a hack that has been introduced to be able to extend the standard library without the need to adapt existing code based on it.
    – Giorgio
    Commented Mar 20, 2014 at 17:37
2

Perhaps the intent was to provide the ability to create mixin classes by replacing the need for injecting static information or functionality via a dependency.

This idea seems related to how you can use extension methods in C# to add implemented functionality to interfaces.

4
  • 1
    Extension methods don't add functionality to interfaces. Extension methods are just syntactic sugar for calling static methods on a class using the convenient list.sort(ordering); form. Commented Mar 20, 2014 at 16:15
  • If you look at the IEnumerable interface in C#, you can see how implementing extension methods to that interface (like LINQ to Objects does) adds functionality for every class that implements IEnumerable. That's what I meant by adding functionality.
    – rae1
    Commented Mar 20, 2014 at 16:24
  • 2
    That's the great thing about extension methods; they give the illusion that you are bolting on functionality to a class or interface. Just don't confuse that with adding actual methods to a class; class methods have access to the private members of an object, extension methods don't (since they're really just another way of calling static methods). Commented Mar 20, 2014 at 16:29
  • 2
    Exactly, and that's why I see some relation on having static or default methods in an interface in Java; the implementation is based on what's available to the interface not the class itself.
    – rae1
    Commented Mar 20, 2014 at 16:46
1

The two main purposes I see in default methods (some use cases serve both purposes):

  1. Syntax sugar. A utility class could serve that purpose, but instance methods are nicer.
  2. Extension of an existing interface. The implementation is generic but sometimes inefficient.

If it was just about the second purpose, you wouldn't see that in a brand new interface like Predicate. All @FunctionalInterface annotated interfaces are required to have exactly one abstract method so that a lambda can implement it. Added default methods like and, or, negate are just utility, and you aren't supposed to override them. However, sometimes static methods would do better.

As for extension of existing interfaces - even there, some new methods are just syntax sugar. Methods of Collection like stream, forEach, removeIf - basically, it's just utility you don't need to override. And then there are methods like spliterator. The default implementation is suboptimal, but hey, at least the code compiles. Only resort to this if your interface is already published and widely used.


As for the static methods, I guess the others cover it quite well: It allows the interface to be its own utility class. Maybe we could get rid of Collections in Java's future? Set.empty() would rock.

Not the answer you're looking for? Browse other questions tagged or ask your own question.