Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
3  
Bonus question: Why didn't they introduce multiple inheritance for classes instead? –  user80577 Mar 21 at 9:34
    
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! –  Jarrod Roberson Sep 4 at 2:02
    
@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 4 at 6:16
    
@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 Oct 1 at 17:29

4 Answers 4

up vote 9 down vote accepted

A good motivating example 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.

share|improve this answer
    
C# overcomes this problem with Extension Methods. –  Robert Harvey Mar 20 at 15:50
1  
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 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 at 9:48
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 Jun 12 at 22:30
1  
@RobertHarvey: I shouldn't have been snarky. Adding CountIsKnown and Count members to IEnumerable<T>, 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<T> to advertise those abilities when the wrapped classes possessed them. Faking such IEnumerable<T> 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 at 22:38

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.

EDIT:

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.

share|improve this answer
2  
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 at 16:23
2  
@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 at 16:44
    
Wait, what? Push and Pop methods on a stack are not going to be static. –  Robert Harvey Mar 20 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 at 16:47
2  
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 at 17:37

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.

share|improve this answer
    
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 Oct 6 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 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 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 Oct 14 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 Oct 14 at 3:00

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.

share|improve this answer
    
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 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 at 16:24
    
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 at 16:29
    
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 at 16:46

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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