JavaRush /Java Blog /Random EN /Default Methods in Java 8: What They Can and Can't Do?
Spitfire
Level 33

Default Methods in Java 8: What They Can and Can't Do?

Published in the Random EN group
Translation of an article written by Peter Verhas dated April 2014. Default Methods in Java 8: What They Can and Can't Do?  - 1From the translator: the term " default method " has just appeared in Java and I'm not sure if there is an established translation into Russian for it. I'll use the term "default method", although I don't think it's ideal. I invite you to discuss a more successful translation.

What is the default method

Now, with the release of Java 8, you can add new methods to interfaces so that the interface remains compatible with the classes that implement it. This is very important if you are developing a library that is used by many programmers from Kyiv to New York. Before Java 8, if you defined an interface in a library, you couldn't add methods to it without risking that some application running your interface would break when it was updated. So, in Java 8 you can no longer be afraid of this? No you can not. Adding a default method to an interface may make some classes unusable. Let's first look at the good things about default methods. In Java 8, the method can be implemented directly in the interface. (Static methods in an interface can now also be implemented, but that's another story.) A method implemented in an interface is called a default method, and is denoted by the default keyword . If a class implements an interface, it can, but is not required to, implement the methods implemented in the interface. The class inherits the default implementation. This is why it is not necessary to modify classes when changing the interface they implement.

Multiple inheritance?

Things get more complicated if a class implements more than one (say, two) interfaces, and they implement the same default method. Which method will the class inherit? The answer is none. In this case, the class must implement the method itself (either directly or by inheriting it from another class). The situation is similar if only one interface has a default method, and in the other the same method is abstract. Java 8 tries to be disciplined and avoid ambiguous situations. If methods are declared in more than one interface, then no default implementation is inherited by the class - you will get a compilation error. Although, you may not get a compilation error if your class is already compiled. Java 8 is not robust enough in this regard. There are reasons for this, which I don’t want to go into discussing (for example: the Java release has already been released and the time for discussions has long passed and in general, this is not the place for them).
  • Let's say you have two interfaces and a class implements both of them.
  • One of the interfaces implements the default method m().
  • You compile all the interfaces and the class.
  • You change an interface that doesn't have an m() method by declaring it as an abstract method.
  • You compile only the modified interface.
  • Start the class.
Default Methods in Java 8: What They Can and Can't Do?  - 2In this case the class works. You can't compile it with the updated interfaces, but it was compiled with older versions and therefore works. Now
  • change the interface with the abstract m() method and add a default implementation.
  • Compile the modified interface.
  • Run class: error.
When there are two interfaces that provide a default implementation of a method, that method cannot be called in a class unless it is implemented by the class itself (again, either on its own or inherited from another class). Default Methods in Java 8: What They Can and Can't Do?  - 3Class compatible. It can be loaded with a modified interface. It may even run until a method is called that has a default implementation in both interfaces.

Example code

Default Methods in Java 8: What They Can and Can't Do?  - 4To demonstrate the above, I created a test directory for the C.java class and 3 subdirectories for the interfaces in the files I1.java and I2.java. The root directory for the test contains the source code for the C.java class. The base directory contains a version of the interfaces that are suitable for execution and compilation: interface I1 has a default method m(); The I2 interface does not yet have any methods. The class has a method mainso we can execute it to test it. It checks if there are any command line arguments, so we can easily execute it either with or without calling the 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 {
}
You can compile and run the class from the command line.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
The compatible directory contains a version of the I2 interface that declares the m() method to be abstract, and also, for technical reasons, an unmodified copy of I1.java.
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
Such a set cannot be used to compile a C class:
~/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. However, we have C.class from a previous compilation and, if we compile the interfaces into the compatible directory, we will have two interfaces that can still be used to run the class:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
The third directory - wrong- contains version I2, which also declares the method m():
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
You don't even have to worry about compilation. Even though the method is declared twice, the class can still be used and run until the m() method is called. This is what we need the command line argument for:
~/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 your library to Java 8 and change your interfaces to include default methods, you probably won't have any problems. At least, that's what Java 8 library developers are hoping for as they add functionality. Applications using your library are still using it for Java 7, where there are no default methods. If several libraries are used together, there is a possibility of conflict. How to avoid it? Design your library's API in the same way as before. Don't become complacent by relying on the capabilities of the default methods. They are a last resort. Choose names carefully to avoid collisions with other interfaces. Let's see how development for Java using this feature will develop.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION