This article shows you the default method of interface in Java 8

Posted by stevebrett on Fri, 17 May 2019 16:42:39 +0200

Java 8 is an important version of Oracle released in March 2014, and its API introduces many new methods on existing interfaces.

For example, Java 8's List interface adds a sort method. Before Java 8, each class that implements the List interface must define the implementation of the sort method, or inherit its implementation from the parent class. Imagine, if the inheritance system of the List interface is very complex, how much maintenance is needed for the entire collection framework!

To this end, a new mechanism is introduced in Java 8: the interface supports the method of declaration band implementation.

Default method

As mentioned earlier, the List interface in Java 8 adds a sort method, whose source code is as follows:

public interface List<E> extends Collection<E> {
    
    // ... Other members
        
    default void sort(Comparator<? super E> c) {
      ...
      ...
    }
}

As you can see, this new sort method has a method body, modified by default modifier, which is the default method of the interface.

Obviously, default methods are not static, so they must be invoked by an instance of the implementation class of the interface.

Next, customize an interface and practice using the default method.

public interface Sized {
    // Ordinary Abstract methods, which are modified by public abstract by default, have no method body
    int size();

    /*
     * Default method, method body
     * Any class that implements the Sized interface inherits the implementation of isEmpty
     */
    default boolean isEmpty() {
        return this.size() == 0;
    }
}

Actually, with the upgrade of JDK version, API is evolving. The default method has been widely used in Java 8 API. The sort method in the List interface above is one of them.

Differentiation from abstract classes

Some students may find that Java 8 has added the interface of default method. Isn't this the abstract class before? In fact, there are differences between the two.

  • A class can inherit only one abstract class; however, a class can implement multiple interfaces.
  • Abstract classes have instance variables, while interfaces can only have class variables

Resolving conflicts

We know that a class in the Java language can inherit only one parent class, but a class can implement multiple interfaces. With the introduction of default methods in Java 8, it is possible for a class to inherit multiple signatures of the same method. In this case, which function does the class choose to use?

To address this multi-inheritance relationship, Java 8 provides the following three rules:

  1. The method priority in the class is the highest, and the method declared in the class or parent class is higher than any method declared as default.
  2. If the first item cannot be judged, then the priority of the sub-interface is higher: when the method signature is the same, the default method interface with the most concrete implementation is preferred, that is, if B inherits A, then B is more specific than A.
  3. Finally, if it is still impossible to judge, classes inheriting multiple interfaces must explicitly override and invoke the desired method to choose which default method to use for implementation.

Let's look at a few examples.

Scene 1:
public interface A {
    default void hello() {
        System.out.println("hello from A");
    }
}
public interface B extends A {
    default void hello() {
        System.out.println("hello from B");
    }
}
public class C implements A, B {
    public static void main(String[] args) {
        new C().hello();
    }
}


Figure 1 shows the UML diagram of this scenario.

Let's look at the three rules above. What does the main() method in class C output?

  1. Rule (1) is not satisfied.
  2. Because B inherits A, B is more specific than A, so the hello() method of B should be chosen. So the program prints out "hello from B".
Scene 2:

What happens if C inherits D as follows?

public class D implements A {

}
public class C extends D implements A, B {
    public static void main(String[] args) {
        new C().hello();
    }
}


Figure 2 shows the UML diagram of this scenario.

Similarly, we look at three rules:

  1. Although C inherits D, the default method of A is not overwritten in D.
  2. Next, the compiler chooses between A and B. Because B is more specific, the program prints out "hello from B".
Scene 3:

Modify the D above slightly:

public class D implements A {
    public void hello() {
        System.out.println("hello from D");
    }
}

What's the result?

Because the method declared in the parent class has a higher priority according to rule (1), the program prints out "hello from D".

Scene 4:

Suppose B is not inheriting A:

public interface A {
    default void hello() {
        System.out.println("hello from A");
    }
}
public interface B {
    default void hello() {
        System.out.println("hello from B");
    }
}
public class C implements A, B {
    public static void main(String[] args) {
        new C().hello();
    }
}

Figure 3 shows the UML diagram of this scenario.

At this point, because the compiler can't recognize whether the implementation of A or B is more specific, a compilation error will be thrown: "C inherits unrelated defaults for Hello ()" from types A and B ".

To resolve conflicts in scenarios like this, you can override the hello() method in C and select the method to call A or B as shown in the method.

The invocation method is as follows:

public class C extends D implements A, B {
    public void hello() {
        // Explicitly select the method in calling interface B
        // Similarly, to call methods in interface A, you can do this: A.super.hello()
        B.super.hello();
    }

    public static void main(String[] args) {
        // Output hello from B
        new C().hello();
    }
}
Scene 5:
public interface A {
    default void hello() {
        System.out.println("hello from A");
    }
}
public interface B extends A{

}
public interface C extends A{

}
public class D implements B, C {
    public void hello() {
        new D().hello();
    }
}

At this point, there is only one way to declare that you can choose, so the program will output "hello from A".

END

Topics: Java Oracle JDK