Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When two interfaces have conflicting return types, why does one method become default?

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?

like image 862
schmod Avatar asked Nov 23 '15 21:11

schmod


People also ask

What happens when two interfaces have the same default method?

If two interfaces contain a method with the same signature but different return types, then it is impossible to implement both the interface simultaneously. According to JLS (§8.4. 2) methods with same signature is not allowed in this case.

CAN interface have more than one default method?

Multiple Defaults With default functions in interfaces, there is a possibility that a class is implementing two interfaces with same default methods. The following code explains how this ambiguity can be resolved. First solution is to create an own method that overrides the default implementation.

Can you implement multiple interfaces and if they have conflicting method names in C#?

The implementation of interface's members will be given by the class who implements the interface implicitly or explicitly. C# allows the implementation of multiple interfaces with the same method name.

What would be the result if a class extends two interface and both have a method with same name and signature?

What would be the result if a class extends two interfaces and both have a method with same name and signature? Lets assume that the class is not implementing that method. Explanation: In case of such conflict, compiler will not be able to link a method call due to ambiguity. It will throw compile time error.


1 Answers

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.

like image 154
Peter Lawrey Avatar answered Oct 16 '22 14:10

Peter Lawrey