【问题标题】:Calling same method name from two different interface - Java从两个不同的接口调用相同的方法名称 - Java
【发布时间】:2013-11-26 09:13:20
【问题描述】:

Java 不允许多重继承来保护菱形问题。它使用接口来处理这个问题。

那么说使用接口的情况吧

interface A{
run();
}

interface B{
run();
}

class C implements A, B{
run() {}   //Which interface we are using?
}

当我们调用C类中的方法run()时,如何判断我们使用的是哪个接口?

【问题讨论】:

  • 而且更重要的是,没关系。
  • 请注意,目前您的方法声明中没有返回类型。如果两个接口run 方法声明了不兼容的返回类型,您将无法实现该接口。
  • 接口是合约,根本没有(实现)代码。换句话说:您正在声明一个必须在其他地方实现的“名称”。

标签: java class interface abstract diamond-problem


【解决方案1】:

你没有。没关系,因为实现不在接口上,而是在类上。所以实现是独一无二的。没有歧义。

重要的是是否每个声明都希望有不同的返回类型:

interface A{
    void run();
}

interface B{
    String run();
}

class C implements A, B{
    ???? run() {}
}

这是在 Java 中遇到多个接口问题的唯一方法。

【讨论】:

  • 在 C 类中,您必须使用 run() 方法,一种使用 void,另一种使用 String。
  • 如果两者都有 void 返回类型怎么办?
  • 更重要的是,如果返回类型也相同,那么我认为,它遵循先到先服务规则,它将从在 implements 关键字之后首先声明的接口覆盖
  • @agpt,你搞错了。您不要覆盖接口的方法,因为方法体尚不存在。您只需使用接口提供的签名实现一个方法。没有先发球规则,因为您没有更换任何东西。
  • @IonutNegru,你不能有两个具有相同名称、相同参数和不同返回类型的方法。将方法签名想象成键盘上的一个键。一旦你按下了键(选择了名字和参数),调用者必须坐下来等待一些结果回来。即使你有两个名为“run”的键,当它们被调用时,它们中的每一个都做不同的事情是没有意义的。
【解决方案2】:

没关系。接口的目的是表明类 C 有一个方法 run()。您可以依赖该方法。

虽然 A 和 B 都指定了 run 方法,但它只实现了一次,所以没有冲突。

当你说你正在使用一个接口时,它实际上意味着你的对象是如何声明的:

   A myobject = new C();
   myobject.run();

   B myObject = new C();
   myobject.run();

在第一种情况下,您“使用”了 A 接口,也就是说,您的代码假设 myobject 对象的类型为 A,并且它有一个名为 run() 的方法。在第二种情况下,您正在“使用”接口 B。

【讨论】:

    【解决方案3】:

    即使您使用相同的方法声明实现多个接口,您的类中也只会有该方法的一个定义。因此,这里没有任何歧义,因为只有一种方法可以调用。

    【讨论】:

      【解决方案4】:

      你的问题没有多大意义。让我们举一个现实的例子。 Human 会哭。 AnimalBaby 会哭。现在你有一个实例John,它既是人类又是动物宝宝,你让它哭泣。发生什么了? John 哭了。它实现了两个接口的cry() 方法这一事实并没有改变哭泣对约翰的意义和作用。

      【讨论】:

        【解决方案5】:

        如果一个类型实现了两个接口,并且每个接口定义了一个签名相同的方法,那么实际上只有一个方法,它们是不可区分的。比如说,如果这两个方法的返回类型有冲突,那么这将是一个编译错误。这是继承、方法覆盖、隐藏和声明的一般规则,不仅适用于两个继承的接口方法之间可能发生的冲突,还适用于接口和超类方法之间的冲突,甚至只是由于泛型的类型擦除引起的冲突.

        兼容性示例

        这是一个例子,你有一个接口 Gift,它有一个 present() 方法(如赠送礼物),还有一个接口 Guest,它也有一个 present() 方法(如客人在场并且不缺席)。

        显赫的约翰尼既是礼物也是客人。

        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!!!"
            }
        }
        

        上面的sn-p编译运行。

        请注意,只有一个@Override 是必需的!!!。这是因为 Gift.present() 和 Guest.present() 是“@Override-equivalent”(JLS 8.4.2)。

        因此,johnny 只有一种 present() 实现,无论您如何对待 johnny,无论是作为礼物还是作为客人,都只有一种方法可以调用。

        不兼容示例

        这是一个示例,其中两个继承的方法不是 @Override 等效的:

        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"
        }
        

        这进一步重申了从接口继承成员必须遵守成员声明的一般规则。在这里,我们让 Gift 和 Guest 定义了带有不兼容返回类型的 present():一个是 void,另一个是布尔值。出于同样的原因,您不能在一种类型中使用 void present() 和 boolean present(),此示例会导致编译错误。

        总结

        您可以继承与@Override 等效的方法,但要遵守方法覆盖和隐藏的通常要求。由于它们是 @Override 等效的,因此实际上只有一种方法可以实现,因此没有什么可区分/选择的。

        编译器不必确定哪个方法适用于哪个接口,因为一旦确定它们是@Override 等效的,它们就是同一个方法。

        解决潜在的不兼容性可能是一项棘手的任务,但这完全是另一个问题。

        参考文献

        http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8.4

        【讨论】:

          【解决方案6】:

          你不知道;不过也没关系,因为接口没有功能。

          当 Java 8 出现时,这将略有改变,因为接口可以具有默认的函数实现;如果有多个默认实现,则可能存在歧义。问题Are defaults in JDK 8 a form of multiple inheritance in Java? 中解释了这是如何解决的。

          【讨论】:

            【解决方案7】:

            两者兼而有之。 C 从两个接口继承了两个方法(恰好是覆盖等效的(8.4.8.4))。 C 然后有一种方法可以覆盖两者,因为签名匹配两者。 (8.4.2)

            但是,人们可能会想知道——仅仅因为两个方法具有相同的名称和参数类型,并不一定意味着它们具有相同的语义。这个名字很可能是一个英文单词,意思是两种不同的东西。如果我们从两个看起来相同但实际上语义不同的超级接口继承了两个方法,那么子类中应该有两个实现怎么办?例如

            interface java.lang.Runnable
                void run()
            
            interface com.animal.Runnable
                void run()
            
            class Dog implements com.animal.Runnable,
                                 java.lang.Runnable // a dog as a task
                ???
            

            我们可以想象一种可以处理这种情况的语言。但是 Java 并没有这样做。如果两种方法足够相似,则假定它们具有相同的语义。幸运的是,在实践中这似乎确实是个问题。

            【讨论】:

              猜你喜欢
              • 2022-01-22
              • 2018-11-04
              • 2011-12-13
              • 2011-07-26
              • 2015-10-19
              • 2017-01-19
              • 2015-03-11
              • 2011-01-23
              相关资源
              最近更新 更多