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

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

Published in the Random EN group
Translation of an article written by Peter Verhas dated April 2014. Default Methods in Java 8: What Can and Can't They Do?  - 1From the translator: the term " default method " in Java has just appeared and I'm not sure if there is an established Russian translation for it. I'll use the term "default method" even though I don't think it's ideal. I invite you to discuss a better 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. Prior to Java 8, if you declared an interface in a library, you couldn't add methods to it without risking that some application that works with your interface breaks when you update it. So, in Java 8, you can no longer be afraid of this? No you can not. Adding a default method to an interface can make some classes unusable. Let's look at the good side of the default methods first. In Java 8, a method can be implemented directly in an interface. (Static methods in an interface can now be implemented too, but that's a different story.) A method implemented in an interface is called a default method and is denoted by the keyword default . If a class implements an interface, it may, 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 all implement the same default method. Which method will inherit the class? 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. In this regard, Java 8 is not strong enough. There are reasons for this, which I do not want to go into discussion (for example:
  • Let's say you have two interfaces and a class implements both of them.
  • One of the interfaces implements the default m() method.
  • You compile all interfaces and class.
  • You change an interface that does not have an m() method by declaring it as an abstract method.
  • Compile only the modified interface.
  • You start the class.
Default Methods in Java 8: What Can and Can't They Do?  - 2In this case the class works. You can't compile it with updated interfaces, but it was compiled with older versions and therefore works. Now
  • change the interface with the m() abstract method and add a default implementation.
  • Compile the modified interface.
  • Run class: error.
When there are two interfaces that provide a default method implementation, that method cannot be called on the 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 Can and Can't They Do?  - 3The class is 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.

Code Example

Default Methods in Java 8: What Can and Can't They Do?  - 4In order to demonstrate the above, I have 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(); interface I2 doesn't have any methods yet. The class has a method mainso we can execute it to test. It checks if there are any command line arguments, so we can easily execute it 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 as 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 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. However, we have a 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 the I2 version, 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 compiling. 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, chances are you won't have any problems. At least that's what Java 8 library developers are hoping for by adding functionality. Applications that use your library so far use it for Java 7 where there are no default methods. If several libraries are used together, there is a possibility of a conflict. How to avoid it? Design your library API in the same way as before. Don't relax by relying on the capabilities of the default methods. They are the last resort. Choose names carefully to avoid collisions with other interfaces. Let's see how development for Java will develop using this feature.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION