The default method for Java8 is described in detail

  • 2020-04-01 03:17:33
  • OfStack

What is the default method?

After Java 8 was released, you could add new methods to the interface, but the interface would still be compatible with its implementation classes. This is important because your library may be widely used by multiple developers. Before Java 8, after an interface was published in the class library, if a new method was added to the interface, applications that implemented the interface were in danger of crashing with the new version of the interface.

With Java 8, isn't that a danger? The answer is no.

Adding a default method to an interface might make some implementation classes unavailable.

First, let's look at the details of the default method.

In Java8, methods in interfaces can be implemented (the static methods in Java8 can also be implemented in interfaces, but that's another topic). The method implemented in the interface is called the default method and is identified by the keyword default as a modifier. When a class implements an interface, it can implement methods that have already been implemented in the interface, but this is not necessary. This class inherits the default method. This is why the implementation class does not need to change when the interface changes.

What about multiple inheritance?

Things get complicated when a class implements more than one (say, two) interfaces that have the same default method. Which default method does the class inherit from? Neither! In this case, the class implements the default method itself (either directly or by inheriting from a class higher up the tree).

The same is true when an interface implements the default method and another interface declares the default method as abstract. Java 8 tries to avoid ambiguity and be rigorous. If a method is declared in more than one interface, then any default implementation will not be inherited and you will get a compile-time error.

However, if you have already compiled your classes, there will be no compile-time errors. At this point, Java 8 is inconsistent. It has its own reasons, for a variety of reasons, and I don't want to elaborate or discuss them in depth here (because: the release has been released, the discussion has been too long, the platform has never had such a discussion).

1. Suppose you have two interfaces, one implementation class.
2. One of the interfaces implements a default method m().
Compile the interface and implementation classes together.
4. Modify the interface that does not contain the m() method and declare the m() method as abstract.
5. Recompile the modified interface separately.
6. Run the implementation class.

< img border = 0 id = theimg onclick = window. The open this. (SRC) SRC = "/ / files.jb51.net/file_images/article/201404/201441783838091.png? 20143178398 ">   The class works fine in the above case. However, you cannot recompile with a modified interface, but you can still compile with an old interface and run. The following

1. Modify the interface containing the abstract method m() to create a default implementation.
2. Compile the modified interface
3. Run class: failed.
When both interfaces provide a default implementation for the same method, the method cannot be called unless the implementation class also implements the default method (either directly or from a class higher up the inheritance tree).

< img border = 0 id = theimg onclick = window. The open this. (SRC) SRC = "/ / files.jb51.net/file_images/article/201404/201441783927122.png? 201431783940 ">   However, this class is compatible. It can be loaded with a new interface, or even executed, as long as it doesn't call a method that has a default implementation in both interfaces.

Example code:

< img border = 0 id = theimg onclick = window. The open this. (SRC) SRC = "/ / files.jb51.net/file_images/article/201404/201441783948085.png? 201431783957 ">

To illustrate the above example, I created a test directory for c.ava with three subdirectories under it for i1.java and i2.java. Test directory contains class C source c. ava. The base directory contains the version of the interface that can be compiled and run. I1 contains m() methods with the default implementation, and I2 contains no methods.

The implementation class contains the main method, so we can execute it in the test. It checks for command-line arguments so that we can easily perform tests that call m() and do not call m().


~/github/test$ cat C.java
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if(args.length == 0 ){
        c.m();
    }
  }
}
~/github/test$ cat base/I1.java
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }
}
~/github/test$ cat base/I2.java
public interface I2 {
}

Compile the run using the following command line:

~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1

The compatible directory contains the I2 interfaces with the abstract method m() and the unmodified I1 interfaces.

~/github/test$ cat compatible/I2.java
public interface I2 {
  void m();
}

This cannot be used to compile class C:
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
   ^
1 error

The error message is very accurate. Since we have the C.class from the previous compilation, if we compile the interfaces in the compatible directory, we still get two interfaces that can run the implementation classes:


~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1

The third directory, called wrong, contains an I2 interface that also defines the m() method:


~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}

We should take the trouble to compile it. Although the m() method is defined twice, the implementation class can still run as long as it does not call the method defined many times, but as soon as we call the m() method, it fails immediately. This is the command line argument we used:


~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting
default methods: I1.m I2.m
    at C.m(C.java)
    at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

conclusion

When you port a class library that adds a default implementation to the interface to a Java 8 environment, you generally have no problems. At least that's what Java8 library developers thought when they added the default method to the collection class. Applications that use your library still rely on Java7's library without the default method. There is a small chance of collisions when working with and modifying multiple different libraries. How can you avoid it?

Design your library as before. Don't take it lightly when you might rely on the default method. Don't use it as a last resort. Choose method names wisely to avoid conflicts with other interfaces. We will learn how to use this feature for development in Java programming.


Related articles: