What Is the "Default" Implementation of Method Defined in an Interface

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();
}
}

default method in interfaces

If you mandate all the implementing classes to override the new method, then it should (if there is no valid default implementation) not be a default method.

But, the approach you have said in which to create a new interface that extends the existing one and making the classes that wish to override the new method(s) by changing the interface type they implement will be problematic since the new method is part of the new interface, you cannot access it when you have the parent/base interface type.

Example:

Existing code:

interface Base {
void m1();
}
class A implements Base {
@Override
public void m1() {
....
}
}
class B implements Base {
@Override
public void m1() {
....
}
}

You create the following interface

interface ExtendedBase extends Base {
void m2();
}

Only class A wants to implement m2. So, it becomes

class A implements ExtendedBase {
@Override
public void m1() {
....
}
@Override
public void m2() {
....
}
}

All is good so far.

When you have a method that takes an object of type Base, you can only call m1 on it (irrespective of you pass object of type A or B)

void someMethod(Base base) {
base.m1();
//base.m2(); won't work
}

To actually make use of m2 elsewhere, you need to change Base to ExtendedBase which would mean that you can no longer pass a B to it. So, you have made all classes implement m2 anyway.

Java Interface : Use default method implementation in implementation classes

Default methods can be overridden

You said:

So only the rest of the methods can be overridden in the implementation classes.

The word "only" there is incorrect. A default method in an interface can indeed be overridden by an implementing class. Thus the word default used as the keyword here, meaning: use this method code if no other implementing code is present at runtime.

Here is a silly contrived example where we define an interface Fruit with a default method isJuicy that returns true. We have two subclasses, Orange and Banana. The first has no override of isJuicy, so its behavior comes from the default method. The second demonstrates that you can override the default method. Here we see the override return false.

package work.basil.example;

public class OverridingDefault
{
public static void main ( String[] args )
{
OverridingDefault app = new OverridingDefault();
app.demo();
}

private void demo ( )
{
System.out.println( "new Orange().isJuicy(): " + new Orange().isJuicy() );
System.out.println( "new Banana().isJuicy(): " + new Banana().isJuicy() );
}

public interface Fruit
{
default boolean isJuicy ( )
{
return true;
}
}

public class Orange implements Fruit
{
}

public class Banana implements Fruit
{
@Override
public boolean isJuicy ( )
{
return false;
}
}
}

When run.

new Orange().isJuicy(): true
new Banana().isJuicy(): false

Prefer abstract classes over default methods

You asked:

Is there a better approach that I can achieve this without default methods and without redundant code in the respective implementation classes?

I do suggest you not use default interface methods for this.

The idea and technology for adding default methods to Java interfaces was not as a feature in itself, but as a solution to another problem: Retrofitting functionality onto existing interfaces to support new lambda features but without breaking the existing code of millions of Java programmers as would happen when otherwise adding methods to existing interfaces. By inventing default method on interfaces, the Java team was able to add more methods to existing interfaces while relieving all existing implementations of the need to implement those new methods. New features, without breaking code, a hallmark of Java.

As stated in State of the Lambda by Brian Goetz 2013-09:

The purpose of default methods (…) is to enable interfaces to be evolved in a compatible manner after their initial publication.

My own opinion is that programmers are generally not going to expect behavior to be built into your interfaces. The classic use of an interface in Java is to define a contract per the method signatures, not define behavior (code). So consider adding adding behavior (code) as default methods only as a last resort.

Instead, define your interface as a contract, with no default methods. At least no default methods at first; you may find later a need as did Brian Goetz and the Java team to add default methods later. But start with only the contract.

Then define an abstract class that implements that interface. Any behavior (code) to be shared across the various subclasses can be moved into this abstract class.

Then go on to define subclasses, concrete classes, that inherit from your abstract class.

With this classic and common approach of interface + abstract class + concrete classes, you have flexibility to make changes, and to make testing easier (with stubs rather than real classes), while efficiently sharing code from one place yet allowing overrides where needed.

Why does an interface's default implementation get called when two classes are in the inheritance chain, and the class in the middle is empty

Classes do not inherit members from interfaces, not even with default implementations. Source.

Note that a class does not inherit members from its interfaces; that is not changed by this feature

As such, since MiddlePrinter does not contain an inherited member for Printer to override, the most concrete implementation of PrintIt() from IPrinterInterface's point of view is its own default implementation.

This is made evident by attempting to apply the override keyword to Printer.PrintIt(). You will get an error that no suitable method was found to override.

Without MiddlePrinter, Printer provides a more concrete implementation by replacing the default.

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.



Related Topics



Leave a reply



Submit