Implementing two interfaces in a class with same method. Which interface method is overridden?
Asked Answered
A

8

309

Two interfaces with same method names and signatures. But implemented by a single class then how the compiler will identify the which method is for which interface?

Ex:

interface A{
  int f();
}

interface B{
  int f();
}

class Test implements A, B{   
  public static void main(String... args) throws Exception{   

  }

  @Override
  public int f() {  // from which interface A or B
    return 0;
  }
}   
Alfano answered 10/5, 2010 at 10:21 Comment(0)
N
435

If a type implements two interfaces, and each interface define a method that has identical signature, then in effect there is only one method, and they are not distinguishable. If, say, the two methods have conflicting return types, then it will be a compilation error. This is the general rule of inheritance, method overriding, hiding, and declarations, and applies also to possible conflicts not only between 2 inherited interface methods, but also an interface and a super class method, or even just conflicts due to type erasure of generics.


Compatibility example

Here's an example where you have an interface Gift, which has a present() method (as in, presenting gifts), and also an interface Guest, which also has a present() method (as in, the guest is present and not absent).

Presentable johnny is both a Gift and a Guest.

public class InterfaceTest {
    interface Gift  { void present(); }
    interface Guest { void present(); }

    interface Presentable extends Gift, Guest { }

    public static void main(String[] args) {
        Presentable johnny = new Presentable() {
            @Override public void present() {
                System.out.println("Heeeereee's Johnny!!!");
            }
        };
        johnny.present();                     // "Heeeereee's Johnny!!!"

        ((Gift) johnny).present();            // "Heeeereee's Johnny!!!"
        ((Guest) johnny).present();           // "Heeeereee's Johnny!!!"

        Gift johnnyAsGift = (Gift) johnny;
        johnnyAsGift.present();               // "Heeeereee's Johnny!!!"

        Guest johnnyAsGuest = (Guest) johnny;
        johnnyAsGuest.present();              // "Heeeereee's Johnny!!!"
    }
}

The above snippet compiles and runs.

Note that there is only one @Override necessary!!!. This is because Gift.present() and Guest.present() are "@Override-equivalent" (JLS 8.4.2).

Thus, johnny only has one implementation of present(), and it doesn't matter how you treat johnny, whether as a Gift or as a Guest, there is only one method to invoke.


Incompatibility example

Here's an example where the two inherited methods are NOT @Override-equivalent:

public class InterfaceTest {
    interface Gift  { void present(); }
    interface Guest { boolean present(); }

    interface Presentable extends Gift, Guest { } // DOES NOT COMPILE!!!
    // "types InterfaceTest.Guest and InterfaceTest.Gift are incompatible;
    //  both define present(), but with unrelated return types"
}

This further reiterates that inheriting members from an interface must obey the general rule of member declarations. Here we have Gift and Guest define present() with incompatible return types: one void the other boolean. For the same reason that you can't an void present() and a boolean present() in one type, this example results in a compilation error.


Summary

You can inherit methods that are @Override-equivalent, subject to the usual requirements of method overriding and hiding. Since they ARE @Override-equivalent, effectively there is only one method to implement, and thus there's nothing to distinguish/select from.

The compiler does not have to identify which method is for which interface, because once they are determined to be @Override-equivalent, they're the same method.

Resolving potential incompatibilities may be a tricky task, but that's another issue altogether.

References

Nitid answered 14/5, 2010 at 8:3 Comment(5)
Thanks - this was helpful. However, I had a further question on incompatibility, which I've posted as a new questionBailey
BTW This changes a little with the support of default methods in Java 8.Jackscrew
Composite classes to resolve potential incompatibilities may be the trick :), but, I never had such problem, and still it is evident it may happen.Ludwog
This article presents a design pattern that can be used to somewhat deal with the situation where you need to implement two Colliding Interface, say Foo and Bar. Basically you have your class implement one of the interfaces, say Foo, and provide a Bar asBar() method to return an inner class that implements the second Bar interface. Not perfect since your class is ultimately not "a Bar", but it could be useful in some circumstances.Archbishopric
im a java developer but c# is really more clever on this : stackoverflow.com/questions/2371178/…Subtorrid
J
43

This was marked as a duplicate to this question https://mcmap.net/q/16324/-implementing-two-interfaces-in-a-class-with-same-method-which-interface-method-is-overridden

You need Java 8 to get a multiple inheritance problem, but it is still not a diamon problem as such.

interface A {
    default void hi() { System.out.println("A"); }
}

interface B {
    default void hi() { System.out.println("B"); }
}

class AB implements A, B { // won't compile
}

new AB().hi(); // won't compile.

As JB Nizet comments you can fix this my overriding.

class AB implements A, B {
    public void hi() { A.super.hi(); }
}

However, you don't have a problem with

interface D extends A { }

interface E extends A { }

interface F extends A {
    default void hi() { System.out.println("F"); }
}

class DE implement D, E { }

new DE().hi(); // prints A

class DEF implement D, E, F { }

new DEF().hi(); // prints F as it is closer in the heirarchy than A.
Jackscrew answered 25/6, 2014 at 5:56 Comment(12)
wow. this is new to me. Why did they have to create default in java 8 ?Ari
To facilitate adding new methods to interfaces (specifically collections interfaces) without breaking 60% of the codebase.Hautrhin
@BoratSagdiyev The biggest reason was to support closues and make the more useful. See Collection.stream(). Have a look at List.sort() docs.oracle.com/javase/8/docs/api/java/util/… They have added a method for all Lists, without having to change any specific implementation. They added Collection.removeIf() which is usefulJackscrew
@TassosBassoukos +1 say you have your own implementation of List, now you can myList.stream() it or myList.sort() it without changing your codeJackscrew
@PeterLawrey: AB won't compile because it has to override hi() (to fix the ambiguity). For example, by implementing it as A.super.hi() to choose to implement it the same way as A.Cinerama
@JBNizet Good point, I have added your suggestion to the answer.Jackscrew
@TassosBassoukos - sorry if this is dumb, but can't you just add new methods with different names to the interface code ?Ari
@BoratSagdiyev Of course you can. But consider what happens if you add another method to java.lang.List - suddenly all the specialized List implementations out in the wild will no longer compile.Hautrhin
@BoratSagdiyev How do you know you are adding a different name? Can you give an example of a method name which is easy to remember, but no-one has ever used before (or ever will) ?Jackscrew
@PeterLawrey the link is broken. It returns a 404Locarno
Question: the difference between default and abstract methods (in your example) is that with the default methods I can use a call to default method like A.super.hi() ?Wards
If you try to bypass javac and directly create the .class file for AB (e.g. using Jasmin), you get the following error when trying to invokeinterface A/hi()V if there are multiple competing defaults: Exception in thread "main" java.lang.AbstractMethodError: Receiver class AB does not define or inherit an implementation of the resolved method 'void hi()' of interface A.Cheltenham
R
23

As far as the compiler is concerned, those two methods are identical. There will be one implementation of both.

This isn't a problem if the two methods are effectively identical, in that they should have the same implementation. If they are contractually different (as per the documentation for each interface), you'll be in trouble.

Renaldorenard answered 10/5, 2010 at 10:29 Comment(2)
It explains why Java does not allow you extends more than one classDzerzhinsk
@ArthurRonald, actually it just looks related. However, IMO, class which extends more than one class can run into Diamond Problem (which is duplicated object state in the most-derived class) and that's most likely why Java took its users away from the troubles. On the other hand, class which implements more than one class can never run into Diamond Problem simply because interface do not provide state to objects. And the problem is purely due to syntax limitations - inability to fully qualify function call.Heater
M
18

There is nothing to identify. Interfaces only proscribe a method name and signature. If both interfaces have a method of exactly the same name and signature, the implementing class can implement both interface methods with a single concrete method.

However, if the semantic contracts of the two interface method are contradicting, you've pretty much lost; you cannot implement both interfaces in a single class then.

Mammilla answered 10/5, 2010 at 10:31 Comment(0)
R
5

Well if they are both the same it doesn't matter. It implements both of them with a single concrete method per interface method.

Recoup answered 10/5, 2010 at 10:27 Comment(0)
T
5

As in interface,we are just declaring methods,concrete class which implements these both interfaces understands is that there is only one method(as you described both have same name in return type). so there should not be an issue with it.You will be able to define that method in concrete class.

But when two interface have a method with the same name but different return type and you implement two methods in concrete class:

Please look at below code:

public interface InterfaceA {
  public void print();
}


public interface InterfaceB {
  public int print();
}

public class ClassAB implements InterfaceA, InterfaceB {
  public void print()
  {
    System.out.println("Inside InterfaceA");
  }
  public int print()
  {
    System.out.println("Inside InterfaceB");
    return 5;
  }
}

when compiler gets method "public void print()" it first looks in InterfaceA and it gets it.But still it gives compile time error that return type is not compatible with method of InterfaceB.

So it goes haywire for compiler.

In this way, you will not be able to implement two interface having a method of same name but different return type.

Trichina answered 7/5, 2014 at 10:36 Comment(0)
M
4

Try implementing the interface as anonymous.

public class MyClass extends MySuperClass implements MyInterface{

MyInterface myInterface = new MyInterface(){

/* Overrided method from interface */
@override
public void method1(){

}

};

/* Overrided method from superclass*/
@override
public void method1(){

}

}
Macdougall answered 7/10, 2011 at 0:39 Comment(0)
D
-2

The following two approaches can also be taken to implement both the duplicate methods and avoid ambiguity -

APPROACH 1:

App.java -

public class App {
    public static void main(String[] args) {
        TestInterface1 testInterface1 = new TestInterface1();
        TestInterface2 testInterface2 = new TestInterface2();
        testInterface1.draw();
        testInterface2.draw();
    }
}

TestInterface1.java -

public class TestInterface1 implements Circle {
    
}

TestInterface2.java -

public class TestInterface2 implements Rectangle {
    
}

Circle.java -

public interface Circle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing circle");
    }
}

Rectangle.java -

public interface Rectangle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing rectangle");
    }
}

Drawable.java -

public interface Drawable {
    default void draw() {
        System.out.println("Drawing");
    }
}

Output -

Drawing circle
Drawing rectangle

APPROACH 2:

App.java -

public class App {
    public static void main(String[] args) {
        
        Circle circle = new Circle() {
                
        };
        Rectangle rectangle = new Rectangle() {
                
        };

        circle.draw();
        rectangle.draw();
    }
}

Circle.java -

public interface Circle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing circle");
    }
}

Rectangle.java -

public interface Rectangle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing rectangle");
    }
}

Drawable.java -

public interface Drawable {
    default void draw() {
        System.out.println("Drawing");
    }
}

Output -

Drawing circle
Drawing rectangle
Duro answered 21/1, 2022 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.