Java 8 Series: Default and Static Interface Methods

Currently in Java 7, if you enhance an interface with a new method declaration, or if you switch to a third-party library’s new version with an enhanced API, then this generally leads to a break of existing implementations in your code. So you must define the new method in existing implementations respectively adapt the versions of depending libraries in order to get rid of compile errors. However, in Java 8 this won’t be always necessary.

Coffee, please!

Let’s assume we are the authors of a library providing an API for developing coffee machines. CoffeeMachine is the library’s most prominent interface:

It declares the method cookCoffee(Recipe recipe) which makes a cup of coffee based on the passed recipe. There exist default recipes for black coffee, white coffee, espresso and cappuccino in the library.

A white coffee, of course!

Now we introduce a new method cookCoffee() to our library which should allow to cook coffee on the basis of the coffee machine’s default recipe:

Our library gets adopted by BroodBrew Machines, a coffee machine manufacturing company from Germany. While switching to the new version of our library their class BroodBrewMachine, which is implementing CoffeeMachine, has to be enhanced with the new requirement:

As you can see the developer of BroodBrewMachine assumed that white coffee is most likely the default choice and thus calls cookCoffee(Recipe recipe) with the white coffee recipe in his implementation. But isn’t it the case that white coffee is almost always the default if you order ‘coffee’ in a restaurant? At least it is in Germany 😉 .

Since Java 8 we as the authors of the coffee machine library will be able to consider that common case and define a default implementation of cookCoffee() within our interface:

Now implementing classes of CoffeeMachine can inherit the default implementation of cookCoffee() and therefore are not required to implement it by themselves (though Italian coffee machine manufactures can still overwrite the method and, for example, specify espresso as the default recipe…).

No sugar, please!

The coffee machine library includes a utility class Recipes which enables to create recipes based on existing ones, though with an de- or increased amount of certain ingredients:

There is a convention to name such helper or utility classes after the plural of their corresponding interface. Thanks to the new static interface function feature such utilities can directly be put to the interface:

We can now make the Recipes class package private. A resulting benefit is that developers inspecting the Recipe interface can now see both, the interface methods as well as the new utility functions at a glance.

The following section describes what you’ll have to consider regarding syntax rules of default and static interface methods.

Syntax Rules

In a nutshell, default and static interface methods have the following syntax:

default METHODSIGNATURE
static METHODSIGNATURE

All interface methods are implicitly public. Therefore it’s not required to prefix default and static interface methods with the public modifier.

An extension respectively implementation of an interface Foo, which has a default method bar(), has the following options:

  • Not specify bar() –> bar() gets simply inherited
  • Redeclare bar() –> bar() becomes abstract
  • Redefine bar() –> bar() gets overwritten

Conclusion

Java’s default and static interface methods can be a big win if they get utilized as intended, namely to

  1. provide definitions of methods that are fairly common and which should, at least in most cases, supersede a redefinition in extending interfaces or implementing classes.
  2. expose utility functionality, which is implemented in package private classes, by means of an interface.

However, I can imagine that developers could bloat interfaces with hundred-line utility functions because they are not able to split logic to private functions within an interface. Other developers might want to have a clean API package without package private utility classes and thus be tempted to put whole definitions of static utility functions into interfaces. Finally some developers could introduce default methods with noop or lazy definitions in order to have less trouble during refactorings, and then forget to remove that interim solutions because the compiler won’t never care…

In the next part of this series we will deal with lambda expressions. Stay tuned 🙂

Other Posts in this Series

References