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?

Mike Partridge
  • 6,587
  • 1
  • 25
  • 39
Mister Smith
  • 2,867
  • 4
  • 21
  • 17
  • 31
    Bonus question: Why didn't they introduce multiple inheritance for classes instead? –  Mar 21 '14 at 09: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! –  Sep 04 '14 at 02: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 Sep 04 '14 at 06:16
  • 11
    @Chris Multiple inheritance of state causes a bunch of problems, especially with memory allocation ([the classic diamond problem](http://en.wikipedia.org/wiki/Multiple_inheritance#The_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 Oct 01 '14 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 Aug 27 '15 at 08:44
  • Is there any reason to use a _default method_ in designing a new `Interface`? Or just to work with old code? – ycomp Feb 14 '16 at 02:27
  • I completely agree that default methods in interfaces is brain-dead. it effectively destroys the meaning of "implements". –  Dec 15 '16 at 22:11
  • I find most of the answers to be either oversimplified, one-sided or a wall of text that still doesn't explain the purpose. I wrote a new answer that tries to explain it in a few sentences, but explain BOTH purposes comprehensively. – Vlasec Dec 24 '16 at 22:03

5 Answers5

62

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.

Gonen I
  • 155
  • 6
soru
  • 3,625
  • 23
  • 15
  • 19
    C# overcomes this problem with Extension Methods. – Robert Harvey Mar 20 '14 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 – ratchet freak Mar 20 '14 at 16:32
  • Sadly, this looks like the most plausible explanation. Maybe they wanted to have "cooler" collection classes, sort of what you have in C#, but they didn't want to or couldn't afford to rewrite or adapt most java.util classes. – Mister Smith Mar 21 '14 at 09:48
  • 5
    I've found [this paper](http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf) where Goetz explains the problem. So i'll be marking this answer as the solution for now. – Mister Smith Mar 21 '14 at 10:01
  • 1
    @RobertHarvey: Create two hundred-million-item List, use `IEnumerable.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`, the return from `Append` could advertise `CountIsKnown` if the constituent collections did, but without such methods that's not possible. – supercat Jun 12 '14 at 22:30
  • 6
    @supercat: I haven't the slightest idea what you are talking about. – Robert Harvey Jun 12 '14 at 22:35
  • 1
    @RobertHarvey: I shouldn't have been snarky. Adding `CountIsKnown` and `Count` members to `IEnumerable`, and having classes which know about them implement them and use them (while the default implementation of `Count` would behave as does the extension method), would make it possible for classes which wrap `IEnumerable` to advertise those abilities when the wrapped classes possessed them. Faking such `IEnumerable` members makes it possible for "direct" implementations to offer a fast counting method (via `ICollection.Count`) but there's no mechanism for wrapped classes... – supercat Jun 12 '14 at 22:38
  • @supercat: Extension methods are just syntactic sugar over static methods, which is all that the answer looks like to me. Apparently it is more than that. – Robert Harvey Jun 12 '14 at 22:40
  • ...to do so. Calling `Count()` on a hundred-million-item `List` is fast. It should be fast even if the collection is wrapped by something like `IEnumerable.Append()`, but there's no decent way to have a wrapper type expose abilities that may or may not be present in wrapped items. – supercat Jun 12 '14 at 22:40
  • I'm not getting it. Why not have it implemented on List class so that we can call `list.sort` via inheritance? – Boyang Feb 04 '16 at 17:33
  • 1
    @CharlesW. List is an interface. – Celos Mar 23 '16 at 10:19
  • why is this a Good Thing? Considering the cost, I don't see much benefit. –  Dec 15 '16 at 22:15
  • 1
    @mobilenik, you can have both multiple and default implementations. Static methods provide single implementation as they can't be overridden, usual interfaces provide no default implementation leading to code duplication. – Basilevs Dec 17 '16 at 00:49
  • 1
    C# extension methods have a major difference between the Java 8 interface default implementations. Extension methods cannot be virtual but Java 8 default implementations can. There are plenty of other small differences but this one can easily make or break your design idea. There are pros and cons to both sides of this, and I've had cases where I was working in one language wishing I had this functionality in the other. It's certainly jarring to see this since we've had SOLID beat into our heads for so many years. – Mohgeroth Feb 01 '17 at 21:17
  • This answer does not really address all the questions asked by SO – Mehraj Malik Apr 05 '18 at 04:59
  • Internally this `default List.sort` uses `Collections.sort(this, c)` :D http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/util/List.java – kAmol Aug 21 '21 at 17:26
55

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.

Pang
  • 313
  • 4
  • 7
ngreen
  • 789
  • 5
  • 10
  • 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: http://mail.openjdk.java.net/pipermail/lambda-dev/2013-March/008435.html – ngreen Oct 06 '14 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 Oct 11 '14 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 Oct 14 '14 at 02: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 Oct 14 '14 at 02: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 Oct 14 '14 at 03:00
  • "This has been a long-standing source of pain in Java, because interfaces tended to be impossible to evolve once they were made public.": I find continuously evolving public interfaces even more painful. So the old Java approach seems more a feature than a bug to me. – Giorgio Sep 09 '17 at 08:49
  • Java 8 made very extensive use of this feature, and I don't recall that being overly painful. How is it more difficult to deal with additions to interfaces vs the alternative, substantial modification which is required due either to breaking changes or being forced to switch to a new version of basically the same thing in a new package or with a new name? – ngreen Sep 11 '17 at 18:59
  • This is the `BEST` answer. – Mehraj Malik Dec 05 '17 at 07:51
44

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.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Doval
  • 15,347
  • 3
  • 43
  • 58
  • 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. – Robert Harvey Mar 20 '14 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 Mar 20 '14 at 16:44
  • Wait, what? Push and Pop methods on a stack are *not* going to be `static`. – Robert Harvey Mar 20 '14 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 Mar 20 '14 at 16:47
  • @Doval: A default implementation is what you normally put in an abstract class, an interface is only meant to define what should be implemented, not how. Now this default implementation can be defined both in interfaces and in abstract classes, which is bad because you have two different concepts that can be used for the same purpose. – Giorgio Mar 20 '14 at 17:36
  • 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 Mar 20 '14 at 17:37
  • @Giorgio See the edit in my post. – Doval Mar 20 '14 at 18:32
  • I don't think multiple inheritance is the main reason behind the change. It was deliberately not included in the language for a reason. Even Gosling stated in 1995 that "JAVA omits many rarely used, poorly understood, confusing features of C++ that in our experience bring more grief than benefit". – Mister Smith Mar 21 '14 at 09:42
  • @MisterSmith This is not multiple inheritance since you can't inherit fields nor two implementations of the same function. – Doval Mar 21 '14 at 11:55
  • @Doval Default methods cannot be final. – Łukasz Wiktor May 08 '14 at 06:31
  • @Doval: By my understanding, the way Java implements default interface implementations can end up with the same sort of deadly diamond as true MI. To prevent the deadly diamond, interfaces should be prohibited from supplying default implementations for methods in derived interfaces. – supercat Oct 11 '14 at 17:37
  • @supercat From what I understand the big problem with MI is that you can end up with two copies of mutable fields and can have some methods manipulating one copy and other methods manipulating the other. Getting two default implementations of a method is arguably much less deadly since you don't inherit any fields and presumably are fine with either default. In any case, I'm curious - which one does Java pick, and does the compiler emit any warnings? – Doval Oct 11 '14 at 18:44
  • @Doval: The fundamental problem is that you end up with two potentially-non-matching definitions for something; whether the thing in question is a field or method doesn't really matter. I think in some cases Java will arbitrarily pick one, but in some other cases might simply come to a screeching halt at run-time (such problems may not be detectable until then, since it's possible that neither of two interfaces has a member `foo` in its initial release, and in later releases both add a member `foo` along with a default implementation. If a class with no `foo` member is built against... – supercat Oct 13 '14 at 15:10
  • ...old versions of the two interfaces, the compiler building that class would have no way of knowing of the conflict. If newer versions of the interfaces are loaded at run-time and an object of the aforementioned class is passed to code that uses `foo`, there would be no basis for selecting either implementation over the other. I think the JVM will try to muddle ahead in such a situation, but that wouldn't always be possible, e.g. if the two interfaces define `foo` with conflicting return types. I really wish Gosling had specified that when interface `foo` declares method `bar`... – supercat Oct 13 '14 at 15:14
  • ...the actual name of the method would be `foo.bar` or something similar, but `bar` would be implicitly regarded as a synonym. While it's generally helpful having `((I1)thing).method()` and `((I2)thing).method()` refer to the same method whenever both are defined, it creates potential conflicts in cases where e.g. a collection type might implement one interface that expects `Count()` to return an `int` and another that expects it to return a `long`, or might implement one interface that expects a `Get()` method to return `null` when nothing is available and another that expects it to throw. – supercat Oct 13 '14 at 15:24
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.

rae1
  • 761
  • 6
  • 11
  • 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. – Robert Harvey Mar 20 '14 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 Mar 20 '14 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). – Robert Harvey Mar 20 '14 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 Mar 20 '14 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.

Vlasec
  • 281
  • 2
  • 8