同じメソッドを使用して、クラスに2つのインターフェースを実装します。どのインターフェイスメソッドがオーバーライドされますか?


235

同じメソッド名とシグニチャーを持つ2つのインターフェース。しかし、単一のクラスによって実装された場合、コンパイラーはどのメソッドがどのインターフェースに対応するかをどのように識別するのでしょうか?

例:

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

回答:


337

型が2つのインターフェースを実装し、それぞれinterfaceが同一のシグニチャーを持つメソッドを定義する場合、実際には1つのメソッドしかなく、それらは区別できません。たとえば、2つのメソッドの戻り値の型が競合している場合は、コンパイルエラーになります。これは、継承、メソッドのオーバーライド、非表示、宣言の一般的なルールであり、2つの継承されたinterfaceメソッド間だけでなくinterface、スーパーclassメソッドとの競合、またはジェネリックの型消去による競合にも適用されます。


互換性の例

ここでは、持っている例ですinterface Giftあり、present()(提示贈り物、のような)方法を、またinterface Guestもあり、present()この方法は、(のように、ゲストが存在し、不在ではありません)。

Presentable johnnyGiftと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!!!"
    }
}

上記のスニペットがコンパイルされて実行されます。

必要なのは1つだけあること注意してください@Override !!! 。これは、Gift.present()およびGuest.present()が「@Override-equivalent」であるためです(JLS 8.4.2)。

このように、johnny 一つだけの実装があるのをpresent()、そしてあなたがどのように扱うかは問題ではないjohnnyかなど、GiftまたはとしてGuest、起動する唯一の方法があります。


非互換性の例

以下は、継承された2つのメソッドが@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"
}

これは、メンバーを継承しinterfaceなければならないことは、メンバー宣言の一般的なルールに従う必要があることをさらに繰り返します。ここでは、持っているGiftGuest定義しpresent()、互換性のない戻り値の型と:1 voidbooleanvoid present()boolean present()を1つの型に入れることができないのと同じ理由で、この例ではコンパイルエラーが発生します。


概要

@Overrideメソッドのオーバーライドと非表示の通常の要件に従って、同等のメソッドを継承できます。彼らは以来ARE @Override換算、効果的に実施するための唯一の方法があるので、から選択/区別するものは何もありません。

コンパイラーは、どのメソッドがどのインターフェースに対応するものであるかを識別する必要はありません。これは@Override、それらが同等であると判断されると、同じメソッドになるためです。

潜在的な非互換性を解決するのは難しい作業ですが、それはまったく別の問題です。

参考文献


ありがとう-これは役に立ちました。しかし、私は非互換性についてさらに質問がありました。それを新しい質問
amaidment

2
ところでこれは、の支援を受けてほとんど変化しdefaultたJava 8の方法
ピーターLawrey

潜在的な非互換性を解決するための複合クラスがトリックかもしれません:)しかし、私はそのような問題を経験したことがなく、それが起こる可能性があることは明らかです。
Aquarius Power

1
この記事をプレゼントするために使用することができ、設計パターンやや使用すると、2つの衝突インタフェースを実装する必要がある状況に対処、と言うFooBar。基本的には、クラスにいずれかのインターフェイス(たとえばFooBar asBar()を実装させ、2番目のBarインターフェイスを実装する内部クラスを返すメソッドを提供します。クラスは最終的に「バー」ではないため、完璧ではありませんが、状況によっては役立つ場合があります。
Javaru

1
Java開発者のイムが、C#では、この上で本当に多くの賢いです:stackoverflow.com/questions/2371178/...
アミールZiarati

25

これはこの質問の複製としてマークされました/programming/24401064/understanding-and-solving-the-diamond-problems-in-java

多重継承の問題を取得するにはJava 8が必要ですが、それ自体はまだダイアモンドの問題ではありません。

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.

JB Nizetがコメントするので、これを私のオーバーライドで修正できます。

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

ただし、問題はありません

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.

ワオ。これは私にとって新しいものです。Java 8でデフォルトを作成する必要があったのはなぜですか?
Erran Morad 2014

1
コードベースの60%を壊すことなく、インターフェース(特にコレクションインターフェース)に新しいメソッドを追加することを容易にするため。
Tassos Bassoukos 2014年

@BoratSagdiyev最大の理由は、閉鎖をサポートし、より便利にすることでした。Collection.stream()を参照してください。List.sort()docs.oracle.com/javase/8/docs/api/java/util/… を見てください。特定の実装を変更する必要なく、すべてのリストのメソッドが追加されています。彼らは便利なCollection.removeIf()を追加しました
Peter Lawrey

@TassosBassoukos +1は、Listの独自の実装があると言い、コードを変更せずにmyList.stream()itまたはmyList.sort()itできるようになりました
Peter Lawrey

3
@PeterLawrey:(hi()あいまいさを修正するために)オーバーライドする必要があるため、ABはコンパイルされません。たとえば、としてそれを実装することでA.super.hi()、それをAと同じ方法を実装することを選択する
JB Nizet

20

コンパイラに関する限り、これら2つの方法は同じです。両方の実装が1つあります。

2つのメソッドが実質的に同一である場合、これらは同じ実装である必要があるため、これは問題ではありません。それらが契約上異なる場合(各インターフェイスのドキュメントに従って)、問題が発生します。


2
Java で複数のクラスを拡張できない理由を説明しています
Arthur Ronald

1
@ArthurRonald、実際には関連しているように見えます。ただし、複数のクラスを拡張する IMO クラスは、Diamond Problem(最も派生したクラスで複製されたオブジェクトの状態)に遭遇する可能性があり、Javaがユーザーをトラブルから遠ざけたのはこのためです。一方、クラスを実装し、複数のクラスができないインタフェースは、オブジェクトの状態を提供していないので、単にダイヤモンド問題に遭遇していません。そして問題は純粋に構文の制限によるものです-関数呼び出しを完全に修飾することができません。
uvsmtid

13

識別する必要はありません。インターフェイスはメソッド名とシグネチャのみを禁止します。両方のインターフェースにまったく同じ名前とシグニチャーのメソッドがある場合、実装クラスは、1つの具象メソッドで両方のインターフェースメソッドを実装できます。

ただし、2つのインターフェイスメソッドのセマンティックコントラクトが矛盾している場合は、かなりの部分が失われています。その場合、単一のクラスで両方のインターフェースを実装することはできません。


4

インターフェースを匿名として実装してみてください。

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(){

}

}

4

インターフェイスと同様に、メソッドを宣言しているだけです。これらの両方のインターフェイスを実装するコンクリートクラスは、メソッドが1つしかないことを理解しています(説明したように、両方とも戻り値の型で同じ名前を持っています)。したがって、問題はありません。そのメソッドを具象クラスで定義できます。

ただし、2つのインターフェースに同じ名前で戻り値の型が異なるメソッドがあり、具象クラスに2つのメソッドを実装する場合:

以下のコードを見てください:

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

コンパイラがメソッド「public void print()」を取得すると、最初にInterfaceAを検索して取得します。ただし、戻り値の型がInterfaceBのメソッドと互換性がないというコンパイル時エラーが発生します。

したがって、コンパイラーにとっては問題になります。

このようにして、同じ名前のメソッドで戻り値の型が異なる2つのインターフェースを実装することはできません。


3

まあ、両方が同じであれば問題ありません。インターフェースメソッドごとに1つの具象メソッドで両方を実装します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.