In Java 8, if I have two interfaces with different (but compatible) return types, reflection tells me that one of the two methods is a default method, even though I haven't actually declared the method as default or provided a method body.
For instance, take the following code snippet:
package com.company;
import java.lang.reflect.Method;
interface BarInterface {}
class Bar implements BarInterface {}
interface FooInterface {
public BarInterface getBar();
}
interface FooInterface2 extends FooInterface {
public Bar getBar();
}
class Foo implements FooInterface2 {
public Bar getBar(){
throw new UnsupportedOperationException();
}
}
public class Main {
public static void main(String[] args) {
for(Method m : FooInterface2.class.getMethods()){
System.out.println(m);
}
}
}
Java 1.8 produces the following output:
public abstract com.company.Bar com.company.FooInterface2.getBar()
public default com.company.BarInterface com.company.FooInterface2.getBar()
This seems odd, not only because both methods are present, but also because one of the methods has suddenly and inexplicably become a default method.
Running the same code in Java 7 yields something a little less unexpected, albeit still confusing, given that both methods have the same signature:
public abstract com.company.Bar com.company.FooInterface2.getBar()
public abstract com.company.BarInterface com.company.FooInterface.getBar()
Java definitely doesn't support multiple return types, so this result is still pretty strange.
The obvious next thought is: "Okay, so maybe this is a special behavior that only applies to interfaces, because these methods have no implementation."
Wrong.
class Foo2 implements FooInterface2 {
public Bar getBar(){
throw new UnsupportedOperationException();
}
}
public class Main {
public static void main(String[] args) {
for(Method m : Foo2.class.getMethods()){
System.out.println(m);
}
}
}
yields
public com.company.Bar com.company.Foo2.getBar()
public com.company.BarInterface com.company.Foo2.getBar()
What's going on here? Why is Java enumerating these as separate methods, and how has one of the interface methods managed to become a default method with no implementation?
It's not a default
method you provide but a bridging method. In the parent interface you have defined.
public BarInterface getBar();
and you must have a method which can be called which implements this.
e.g.
FooInterface fi = new Foo();
BarInterface bi = fi.getBar(); // calls BarInterface getBar()
However, you also need to be able to call it's co-variant return type.
FooInterface2 fi = new Foo();
Bar bar = fi.getBar(); // calls Bar getBar()
These are the same method, only difference is that one calls the other and cast the return value. It's the method which appears to have a default
implementation as it is on the interface which does this.
Note: if you have multiple levels of interfaces/class and each has a different return type, the number of methods accumulates.
The reason it does this is that the JVM allows having multiple methods with different return type because the return type is part of the signature. I'e the caller has to state which return type it is expecting and the JVM doesn't actually understand co-variant return types.