Purpose of Default or Defender Methods in Java 8

Purpose of Default or Defender methods in Java 8

Besides having the possibility of adding methods to the interface in future versions, there is the important point of allowing an interface to stay a functional interface even if it has more than one method.

A functional interface has only one non-default abstract method which can be implemented via a lambda expression. One example is the Predicate interface which has only one abstract method (test) while providing default methods for negating a Predicate or combining it with another Predicate. Without default methods these methods had to be provided in another utility class like the pre-Java 8 Collections class (as you don’t want to give up the possibility of lambda implementations for such an interface).

Java 8 Need of Defender (default) Methods

The problem with sharing functionality by placing it in an abstract base class is that a class can derive from exactly one base class. This is a limitation in cases when you would like to inherit functionality from more than one base.

Sharing functionality through an abstract base class may also become a problem when you need to implement an interface from a class that already has a base class. In this case you cannot derive your new class at all, because you must pick one of the two bases, when presumably you want both.

Default methods solve this problem with elegance: placing your common implementation into the default method allows you to share the code without limitations.

You can think of the main difference between default methods and inheriting an abstract class as the difference between sharing functionality horizontally across siblings that implement the same interface, or vertically among children that inherit from the same base class.

Here is an examoke: consider an interface that looks like ResultSet of JDBC, which has two ways of accessing the same column - by name and by index. The interface could be coded up like this:

interface ResultSet2 {
int findColumn(String columnLabel);
String getString(int index);
long getLong(int index);
default long getLong(String columnLabel) {
return getLong(findColumn(columnLabel));
}
default String getString(String columnLabel) {
return getString(findColumn(columnLabel));
}
}

Anyone implementing ResultSet2 would have to implement three methods, and get the remaining two for free. They would have an option to provide an alternative implementation, but that would be optional.

Why the use of default keyword in java8

It makes the intention clear. You can't accidentally create a default implementation for a method. Just like abstract methods require the keyword, instead of just being methods without implementation.

A safety precaution for the careless programmers.

why Interface Default methods?

To overcome that we could have had one class providing implementation of these default methods and then implementing class like arraylist etc could have extended that.

Your suggestion would work only for standard JDK classes (since they usually extends some base classes such as AbstractCollection and AbstractList, were the implementation of the new methods can be added).

What about custom classes that implement JDK interfaces? If, for example, you have a class that implements List but doesn't extend some JDK List implementation, you should be able to switch to Java 8 without having to implement new methods in your class.

With default implementations of new methods in the List interface, you don't have to touch your custom class. You can later add a custom implementation to those methods if you are not satisfied by the default implementation.

Why is adding default methods to interfaces in Java 8 a good design choice and what are the alternatives

Why was this language feature introduced?

It was added primarily to let you add methods to existing interfaces that are already in use without breaking everyone's code, but also to share method implementations "horizontally" across classes implementing the same interface (as opposed to "vertical" sharing through inheritance).

What key new features does it support? (for instance Splititerators)

java.util.Collection<T>.stream()

What other alternatives were there to support those language features? For example, why not create a new interface SplitIterable that extends Iterable?

You could opt for entirely new interfaces, and continue with the associated static helper class (e.g. Collection<T> interface and its Collections helper class). This wouldn't be terrible - in fact, one could argue that default methods are purely a syntactic sugar on top of static methods*. However, default methods generally provide for better readability.

What would be the impact of implementing those alternatives (proliferation of interfaces?)

You would end up with a less consistent library, and a less readable language, but it wouldn't be the end of the world. A bigger concern, as pointed out by Joachim Sauer, is that interface implementations would have no way to override implementation from the static helper class. That would take away flexibility.

Should I provide a default implementation for a method in the first edition of interface when it is possible to implement it as a composition of other methods?

You should do it only if you need to share implementation "horizontally". If a method provides essential behavior of the implementation, do not provide a default for it.

* This would be an oversimplification, because default methods remain virtual. Thanks Brian Goetz for the comment.

What is the purpose of the default keyword in Java?

It's a new feature in Java 8 which allows an interface to provide an implementation. Described in Java 8 JLS-13.5.6. Interface Method Declarations which reads (in part)

Adding a default method, or changing a method from abstract to default, does not break compatibility with pre-existing binaries, but may cause an IncompatibleClassChangeError if a pre-existing binary attempts to invoke the method. This error occurs if the qualifying type, T, is a subtype of two interfaces, I and J, where both I and J declare a default method with the same signature and result, and neither I nor J is a subinterface of the other.

What's New in JDK 8 says (in part)

Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility with code written for older versions of those interfaces.

Java default interface methods concrete use cases

Brian Goetz and I covered some of this at our JavaOne 2015 talk, API Design with Java 8 Lambda and Streams. Despite the title, there is some material at the end about default methods.

Slides: https://stuartmarks.files.wordpress.com/2015/10/con6851-api-design-v2.pdf

Video: https://youtu.be/o10ETyiNIsM?t=24m

I'll summarize here what we said about default methods.

Interface Evolution

The primary use case of default methods is interface evolution. Mainly, this is the ability to add methods to interfaces without breaking backward compatibility. As noted in the question, this was most prominently employed to add methods allowing conversion of Collections to Streams and to add lambda-based APIs to Collections.

There are several other use cases, though.

Optional Methods

Sometimes interface methods are logically "optional". Consider mutator methods on immutable collections, for example. Of course, an implementation is required, but usually what it will do in such cases is to throw an exception. This can easily be done in a default method. Implementations can inherit the exception-throwing method if they don't want to provide it, or they can override it if they want to provide an implementation. Example: Iterator.remove.

Convenience Methods

Sometimes a method is provided for the convenience of callers, and there is an obvious and optimal implementation. This implementation can be provided by a default method. It's legal for an implementation to override the default, but there's generally no reason, so implementations will usually inherit it. Examples: Comparator.reversed, Spliterator.getExactSizeIfKnown, Spliterator.hasCharacteristics. Note that Spliterator was introduced in Java 8, including the default methods, so this clearly wasn't a case of interface evolution.

Simple Implementation, Intended to be Overridden

A default method can provide a simple, general implementation that works for all implementations, but that is probably suboptimal. This assists implementations during initial bring-up, because they can inherit the default and be assured of correct operation. However, in the long term, implementations will probably want to override the default and provide an improved, customized implementation.

Example: List.sort. The default implementation copies the list elements to a temporary array, sorts the array, and copies the elements back to the list. This is a correct implementation, and sometimes it can't be improved upon (e.g. for LinkedList). However, ArrayList overrides sort and sorts its internal array in-place. This avoids the copying overhead.

Now, obviously sort was retrofitted onto List and ArrayList in Java 8, so the evolution didn't happen this way. But you could easily imagine bringing up a new List implementation. You'd probably initially inherit the sort default implementation while you're getting the basics implemented properly. Later on, you might consider implementing a customized sort algorithm that's tuned to your new implementation's internal data organization.

Why we need default methods in Java?

This is for backwards compatibility.

If you have an interface that other people have implemented then if you add a new method to the interface all existing implementations are broken.

By adding a new method with a default implementation you remaining source-compatible with existing implementations.

For a slightly simple/contrived example that should hopefully demonstrate this let us say you created a library:

void drawSomething(Thing thing) {
}

interface Thing {
Color getColor();
Image getBackgroundImage();
}

Now you come to do a new version of your library and you want to add the concept of border colors, that's easy to add to the interface:

interface Thing {
Color getColor();
Color getBorderColor();
Image getBackgroundImage();
}

But the problem is that every single person using your library has to go back through every single Skin implementation they ever did and add this new method.

If instead you provided a default implementation to getBorderColor that just called getColor then everything "just works".

What is the default implementation of method defined in an Interface?

From https://dzone.com/articles/interface-default-methods-java

Java 8 introduces “Default Method” or (Defender methods) new feature, which allows developer to add new methods to the interfaces without breaking the existing implementation of these interface. It provides flexibility to allow interface define implementation which will use as default in the situation where a concrete class fails to provide an implementation for that method.

public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
}

public class ClassAB implements A {
}

There is one common question that people ask about default methods when they hear about the new feature for the first time:

What if the class implements two interfaces and both those interfaces define a default method with the same signature?

Example to illustrate this situation:

public interface A {  
default void foo(){
System.out.println("Calling A.foo()");
}
}

public interface B {
default void foo(){
System.out.println("Calling B.foo()");
}
}

public class ClassAB implements A, B {

}

This code fails to compile with the following result:

java: class Clazz inherits unrelated defaults for foo() from types A and B

To fix that, in Clazz, we have to resolve it manually by overriding the conflicting method:

public class Clazz implements A, B {
public void foo(){}
}

But what if we would like to call the default implementation of method foo() from interface A instead of implementing our own.

It is possible to refer to A#foo() as follows:

public class Clazz implements A, B {
public void foo(){
A.super.foo();
}
}


Related Topics



Leave a reply



Submit