When to Use: Java 8+ Interface Default Method, Vs. Abstract Method

When to use: Java 8+ interface default method, vs. abstract method

There's a lot more to abstract classes than default method implementations (such as private state), but as of Java 8, whenever you have the choice of either, you should go with the defender (aka. default) method in the interface.

The constraint on the default method is that it can be implemented only in the terms of calls to other interface methods, with no reference to a particular implementation's state. So the main use case is higher-level and convenience methods.

The good thing about this new feature is that, where before you were forced to use an abstract class for the convenience methods, thus constraining the implementor to single inheritance, now you can have a really clean design with just the interface and a minimum of implementation effort forced on the programmer.

The original motivation to introduce default methods to Java 8 was the desire to extend the Collections Framework interfaces with lambda-oriented methods without breaking any existing implementations. Although this is more relevant to the authors of public libraries, you may find the same feature useful in your project as well. You've got one centralized place where to add new convenience and you don't have to rely on how the rest of the type hierarchy looks.

Java 8 -- interfaces with default methods vs abstract classes

Here are some reasons to choose an abstract class over an interface. The ones you list - private fields, etc., are some of the major ones. These are a few more subtle ones

  • Type clarity. You can only extend one class. This makes it clearer what your object is an how to use it.
  • The diamond problem. You have to implement all of the default methods in an interface to help avoid the diamond problem. If the interface is Collections, this can be a huge pain since there are a billion of them. With an abstract class, you only overwrite what you need to
  • In Java 8, there are lambda expressions, and the old routine of passing around an interface with a method to implement has been usurped. Still, when you see an interface with a default method, this can interpreted in ways you might not intend

Here's what Oracle has to say on the subject:

Which should you use, abstract classes or interfaces?
Consider using abstract classes if any of these statements apply to
your situation:

  • You want to share code among several closely related classes.
  • You expect that classes that extend your abstract class have many common methods or fields, or require access modifiers other than
    public (such as protected and private).
  • You want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the
    object to which they belong.

In this article, Orace defends the distinction between the two type systems
https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

EDIT To clarify the "diamond problem" bullet
The problem is described here (and many other places)
http://www.lambdafaq.org/what-about-the-diamond-problem/

The problem occurs when you inherit from two places that declare the same method, and you have to pick one when resolving a function call. This was never an issue in Java 7-, since you could only extend one class and interfaces had no methods.

The resolution tactic now is a little complicated, but here is a good description of it:
(from http://blog.loxal.net/2013/05/java-8-default-interface.html)

To address the diamond problem there is a precedence in which order an
implementation is used: only if the class implements all default /
optional methods of its interfaces, the code can be compiled and the
implementations of this class are used. Otherwise the compiler tries
to patch the missing implementation(s) with interface's default
implementation. And if there are multiple default implementations of a
method, then the diamond problem occurs and the compiler rejects the
compilation. Also if the class implements an interface's default
method, the implementation of the class will be used instead of
interfaces's default implementation.

If you stick to abstract classes, you will never run into this problem. However, if your object is required to implement two interfaces (because it needs to be added to lists that are expecting those types, e.g.), and those interfaces have conflicting method signatures, you will wind up having to redefine a whole bunch of methods even if that means you're just making super calls, which sort of defeats the point of inheritance-based dynamic dispatch. It isn't a deal-breaker, but something to consider when structuring a complex Java project

Java 8 default methods vs. non-abstract methods in abstract classes

non-abstract methods in abstract classes will be called when it's concrete subclass calls super() if it is overridden. So there are multiple possibilities. If method is not overridden then the super class method will be executed. if we use super() in the concrete subclass method then the overridden method with the super class method will be executed.

Where as Java 8 interface default methods are completely different. It provided a choice to the developers to implement the method in the implementing class or not. If the function is not implemented then and only then the default method will be executed.

Possible Use Case :

The most important use case for this new feature in the JDK libraries is the possibility to extend existing interfaces without breaking existing implementers: adding a new abstract method to an interface would require all implementing classes to implement that new method.(Source)

Java 8 Interface vs Abstract Class

That sentence means that a default method is implemented inside an interface, so it doesn't have any access to a real state of an object but just to what the interface itself exposes, since an interface can't declare instance variables.

For example:

abstract class Foo {
int result;
int getResult() { return result; }
}

This can't be done in an interface because you can't have any member variable. The only thing you can do is to combine multiple interface methods, which is what it is specified as convenience / higher-level methods, eg:

interface Foo {
void preProcess();
void process();
void postProcess();

default void processAll() {
preProcess();
process();
postProcess();
}
}

When to use interface vs abstract class after Java 8

default methods on interface

Apparently you are referring to the feature of “default methods” implementing behavior in an interface.

You should understand that the feature was added as a way around this dilemma: How to retroactively add features leveraging streams and lambda on existing interfaces without breaking existing classes that implement those interfaces?

Many new methods were added to those interfaces such as in the Java Collections Framework. Adding methods to an existing interface would automatically break all classes implementing the interface that are lacking the newly-required methods. Being able to provide a fallback, to give an implementation where one is now required but not yet existing, would resolve the dilemma. Thus « default methods » were born.

To quote from the Oracle tutorial linked above:

Default 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.

To quote this Answer by Brian Goetz, Java Language Architect at Oracle:

The proximate reason for adding default methods to interfaces was to support interface evolution

So this feature of adding default behavior to an interface was not meant to be a new mainstream feature in and of itself. The intent of default was not to replace abstract.

Indeed, some experienced Java experts have recommended against making a habit of writing default methods on an interface. They recommend pretty much ignoring that feature of the Java language. Continue to think of interfaces as simply defining a contract, and abstract classes as providing partial implementations meant to be completed in a subclass.

You are certainly free to write your own default methods on your interfaces. And certainly you should do so if you are in a similar situation of having published an interface that others may have implemented, and now you want to add methods. But unnecessarily adding default methods is likely to confuse other programmers who expect partial implementations on an abstract class rather than an interface.

Four situations calling for default method on an interface

In that same post linked above, Brian Goetz suggests three other cases beyond interface evolution where default methods on an interface may be appropriate. Here is a quick mention; see his post for details.

  • Optional methods - The default method throws an UnsupportedOperationException because we expect implementations of this interface to more often not want to implement this method.
  • Convenience methods
  • Combinators

Start with interface, move to abstract class for shared code

As for choosing between an interface and abstract class:

  • Generally start with an interface. Or several interfaces if you want various implementing classes to mix various contracts (see mixin).
    • Think twice before adding a default method. Consider if your situation meets one of the four cases recommended by Brian Goetz as discussed above.
  • If you come to realize that you have duplicated code across multiple classes, then consider centralizing that shared code to an abstract class to be used across subclasses.
    • Alternatively, use composition rather than inheritance (discussed below).

For example, you might have a class for domestic ShippingLabelUS as well as ShippingLabelCanada and ShippingLabelOverseas. All three need to convert between imperial pounds and metric kilograms. You find yourself copying that code between the classes. At this point you might consider having all three classes extend from abstract class ShippingLabel where a single copy of the weight conversion methods live.

While designing your API keep in mind that Java, like most OOP languages, has single-inheritance. So your subclasses are limited to extending only one class. To be a bit more specific about the single-versus-multiple inheritance, I will quote Brian Goetz from this PDF of a slide deck:

[regarding default methods on an interface]

Wait,is this multiple inheritance in Java?

• Java always had multiple inheritance of types

• This adds multiple inheritance of behavior

• But not of state, where most of the trouble comes from

Composition over inheritance

An alternative to using an abstract class for shared behavior is creating a separate class for specific behavior, then adding an object of that separate class to be kept as a member field on the larger class. Wise programmers often share this pearl of wisdom: Prefer composition over inheritance.

Regarding the shipping label example above, you could create a WeightConverter class, an object of which would be a member of each of the three label classes. In this arrangement, no need for the abstract class.

Does Java have plan that default method (java8) Substitute for Abstract Class?

Default methods can't substitute abstract classes, as abstract classes can (and often do) have fields. Interfaces can only contain behaviour and not state, which is unlikely to change in the future as multiple inheritance of state in Java is seen (rightly or wrongly) as evil.

They can also have final methods, which is another thing you can't mimic with default methods.

If anything, interfaces with default methods resemble traits rather than abstract classes, but the match isn't perfect. Using interfaces as traits is something that has to be done very carefully and knowing the limitations they come with. (Such as any implementing class can override a default method, potentially ruining the trait.)

More on this here.

Is Interface remain fully abstract after adding default method in java 1.8?

Even if you have only one default method in your interface, it will be abstract. You will have to provide implementing class to instantiate an object. Note that default methods added in Java 8 has special purpose. From Java doc:

Default 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.

So you should use default methods judiciously.



Related Topics



Leave a reply



Submit