Translation of an article written by Peter Verhas dated April 2014. From 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.
- change the interface with the m() abstract method and add a default implementation.
- Compile the modified interface.
- Run class: error.
Code Example
In 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 methodmain
so 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$
GO TO FULL VERSION