Default methods in Java 8

Default Methods are added to Java 8 largely to support library designers by enabling them to write more evolvable interfaces. They’re important because you’ll increasingly encounter them in interfaces, but because relatively few programmers will need to write default methods themselves and because they facilitate program evolution rather than helping write any particular program, we keep the explanation here short and example-based: Consider the following example of Java 8 code:

List < Apple > heavyApples1 = 
  inventory.stream().filter((Apple a) -> a.getWeight() > 150).collect(toList());

List < Apple > heavyApples2 = 
  inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150).collect(toList());

 

A List<T> prior to Java 8 doesn’t have stream or parallelStream methods — and neither does the Collection<T> interface that it implements— because these methods hadn’t been conceived of! And without these methods this code won’t compile. The simplest solution, which you might employ for your own interfaces, would have been for the Java 8 designers simply to add the stream method to the Collection interface and add the implementation in the ArrayList class. But doing this would have been a nightmare for users. There are many alternative collection frameworks that implement interfaces from the Collections API. Adding a new method to an interface means all concrete classes must provide an implementation for it. Language designers have no control on all existing implementations of Collections, so you have a bit of a dilemma: how can you evolve published interfaces without disrupting existing implementations?


Parallelism in Java and no shared mutable state People have always said parallelism in Java is difficult, and all this stuff about synchronized is error prone. Where’s the magic bullet in Java 8? There are actually two magic bullets.

  • First, the library handles partitioning — breaking down a big stream into several smaller streams to be processed in parallel for you.
  • Second, this parallelism almost for free from streams works only if the methods passed to library methods like filter don’t interact, for example, by having mutable shared objects. But it turns out that this restriction feels quite natural as a coder (see, by way of example, our Apple::isGreenApple example). Indeed, although the primary meaning of functional in functional programming means “using functions as first class values,” it often has a secondary nuance of “no interaction during execution between components.”

The Java 8 solution is to break the last link — an interface can now contain method signatures for which an implementing class doesn’t provide an implementation! So who implements them? The missing method bodies are given as part of the interface (hence default implementations) rather than in the implementing class. This provides a way for an interface designer to enlarge an interface beyond those methods that were originally planned — without breaking existing code. Java 8 uses the new default keyword in the interface specification to achieve this.  For example, in Java 8 you can now call the sort method directly on a List. This is made possible with the following default method in the Java 8 List interface, which calls the static method Collections.sort:

default void sort(Comparator<? super E> c) {    Collections.sort(this, c); }


This means any concrete classes of List don’t have to explicitly implement sort, whereas in previous Java versions such concrete classes would fail to recompile unless they provided an implementation for sort. But wait a second — a single class can implement multiple interfaces, right? So if you have multiple default implementations in several interfaces, does that mean you have a form of multiple inheritance in Java? Yes, to some extent! We show in chapter 9 that there are some restrictions that prevent issues such as the infamous diamond inheritance problem in C++.

JAVA-8 STREAM LAMDA