Java-インターフェース実装でのメソッド名の衝突


88

2つのインターフェースがあり、どちらも目的がまったく異なりますが、メソッドの署名が同じである場合、両方のインターフェースに役立つ単一のメソッドを記述したり、メソッドに複雑なロジックを記述したりせずに、クラスに両方を実装させるにはどうすればよいですか?呼び出しが行われているオブジェクトのタイプをチェックし、適切なコードを呼び出す実装?

C#では、これは明示的なインターフェイス実装と呼ばれるものによって克服されます。Javaに同等の方法はありますか?


37
とき1クラスが行うのと同じシグネチャを持つ2つのメソッドを実装する必要があります異なるものを、そしてあなたのクラスがされ、ほぼ確実にあまりにも多くのことをやって。
ヨアヒムザウアー2010

15
上記は必ずしもIMOに当てはまるとは限りません。単一のクラスで、外部コントラクトを確認する必要がある(したがって署名を制約する)メソッドが必要になる場合がありますが、実装は異なります。実際、これらは重要なクラスを設計する際の一般的な要件です。オーバーロードとオーバーライドは必然的に、署名が異ならない、またはごくわずかに異なる可能性のある異なることを行うメソッドを可能にするメカニズムです。ここにあるのは、サブクラス化を許可しない/さらには許可しないという点で少し制限があります。署名のわずかなバリエーション。
バスカー2010

1
これらのクラスとメソッドが何であるかを知りたいと思います。
ウリ

2
従来の「Address」クラスが、データモデルから文字列を返すだけのgetName()メソッドを持つPersonインターフェイスとFirmインターフェイスを実装しているようなケースに遭遇しました。新しいビジネス要件では、Person.getName()が「姓、名」の形式の文字列を返すように指定されていました。多くの議論の後、データは代わりにデータベースで再フォーマットされました。
ベルウッド2012年

12
クラスがほぼ確実にあまりにも多くのことをしていると述べるだけでは、建設的ではありません。私のクラスには2つの異なるインターフェースからのメホド名の衝突があり、私のクラスはあまり多くのことを行っていないので、今まさにこのケースがあります。目的は非常に似ていますが、少し異なることをします。質問者が悪いソフトウェア設計を実装していると非難することによって、明らかに深刻な障害のあるプログラミング言語を擁護しようとしないでください!
j00hi 2014

回答:


75

いいえ、Javaの1つのクラスに2つの異なる方法で同じメソッドを実装する方法はありません。

それは多くの混乱する状況につながる可能性があり、それがJavaがそれを許可していない理由です。

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

class Impl implements ISomething, ISomething2 {
   void doSomething() {} // There can only be one implementation of this method.
}

あなたができることは、それぞれが異なるインターフェースを実装する2つのクラスからクラスを構成することです。その場合、その1つのクラスは両方のインターフェースの動作を持ちます。

class CompositeClass {
    ISomething class1;
    ISomething2 class2;
    void doSomething1(){class1.doSomething();}
    void doSomething2(){class2.doSomething();}
}

9
しかし、このように、インターフェイス(ISomethingまたはISomething2)の参照が期待される場所にCompositeClassのインスタンスを渡すことはできませんか?クライアントコードがインスタンスを適切なインターフェイスにキャストできるとは期待できないので、この制限によって何かを失うことはありませんか?また、このように、それぞれのインターフェイスを実際に実装するクラスを作成する場合、コードを1つのクラスにまとめる利点が失われることに注意してください。これは、深刻な障害となる場合があります。
バスカー2010

9
@Bhaskar、あなたは有効なポイントを作ります。私が持っている最高のアドバイスは追加ですISomething1 CompositeClass.asInterface1();ISomething2 CompositeClass.asInterface2();、そのクラスにメソッドを。次に、複合クラスからどちらか一方を取得できます。ただし、この問題に対する優れた解決策はありません。
jjnguy 2010

1
これがもたらす可能性のある混乱した状況について言えば、例を挙げていただけますか?メソッド名に追加されたインターフェイス名を、衝突/混乱を回避できる追加のスコープ解決と考えることはできませんか?
バスカー2010

@Bhaskarクラスが単一責任の原則に準拠しているとよいでしょう。2つの非常に異なるインターフェイスを実装するクラスが存在する場合、単一責任を処理するためにクラスを分割するように設計を作り直す必要があると思います。
アニルダンJ 2013

1
どのように混乱し、それは次のような何かをできるように、本当に、だろうpublic long getCountAsLong() implements interface2.getCount {...}[場合には、インターフェイスが必要ですlongが、クラスのユーザーが期待するint]またはprivate void AddStub(T newObj) implements coolectionInterface.Add[想定しcollectionInterfaceているcanAdd()方法を、このクラスのすべてのインスタンスのためにそれを返しますかfalse]?
スーパーキャット2013

13

Javaでこれを解決する実際の方法はありません。回避策として内部クラスを使用できます。

interface Alfa { void m(); }
interface Beta { void m(); }
class AlfaBeta implements Alfa {
    private int value;
    public void m() { ++value; } // Alfa.m()
    public Beta asBeta() {
        return new Beta(){
            public void m() { --value; } // Beta.m()
        };
    }
}

からAlfaBetaへのキャストは許可されていませんがBeta、ダウンキャストは一般的に悪であり、AlfaインスタンスにもBetaアスペクトがあることが予想される場合は、何らかの理由(通常は最適化が唯一の有効な理由)で可能になりますそれをに変換するにはBeta、その中にAlfawithのサブインターフェイスを作成できますBeta asBeta()


内部クラスではなく匿名クラスを意味しますか?
Zaid Masud

2
@ZaidMasud囲んでいるオブジェクトのプライベート状態にアクセスできるため、内部クラスを意味します)。もちろん、これらの内部クラスは匿名にすることもできます。
gustafc 2012

11

この問題が発生している場合は、委任を使用する必要がある場所で継承を使用している可能性があります。同じ基になるデータモデルに対して、類似しているものの2つの異なるインターフェイスを提供する必要がある場合は、ビューを使用して、他のインターフェイスを使用してデータへのアクセスを安価に提供する必要があります。

後者の場合の具体的な例を示すために、CollectionMyCollection(から継承せずCollection、互換性のないインターフェイスを持つ)の両方を実装するとします。同じ基になるデータを使用して、との軽量実装を提供するCollection getCollectionView()MyCollection getMyCollectionView()関数を提供できます。CollectionMyCollection

前者の場合...整数の配列と文字列の配列が本当に必要だとします。List<Integer>との両方から継承する代わりにList<String>、タイプの1つのメンバーとタイプのList<Integer>別のメンバーList<String>を用意し、両方から継承しようとするのではなく、それらのメンバーを参照する必要があります。整数のリストのみが必要な場合でも、この場合は継承よりも構成/委任を使用することをお勧めします。


そうは思いません。それらと互換性を持たせるために異なるインターフェースを実装する必要があるライブラリーを忘れています。複数の競合するライブラリを頻繁に使用することでこれに遭遇する可能性があり、独自のコードでこれに遭遇する可能性があります。
ナイトプール2016

1
@nightpoolそれぞれが異なるインターフェースを必要とする複数のライブラリーを使用する場合でも、単一のオブジェクトが両方のインターフェースを実装する必要はありません。2つの異なるインターフェースのそれぞれを返すためのアクセサーをオブジェクトに持たせることができます(そして、オブジェクトを基礎となるライブラリーの1つに渡すときに適切なアクセサーを呼び出します)。
Michael Aaron Safyan 2016

1

「古典的な」Javaの問題も私のAndroid開発に影響を与えます...
理由は単純なようです:
使用する必要のあるフレームワーク/ライブラリが増えると、物事が制御不能になる可能性が高くなります...

私の場合、BootStrapperAppクラスがあります継承android.app.Application
同じクラスでも実装する必要があり、一方、プラットフォーム集積得るためにMVVMフレームワークのインタフェースを。
メソッドの衝突はgetString()メソッドで発生しました。これは両方のインターフェイスによってアナウンスされ、異なるコンテキストで異なる実装が必要です。
回避策(醜い..IMO)は、内部クラスを使用してすべてのプラットフォームを実装していますメソッド、1つのマイナーなメソッドシグネチャの競合のために...場合によっては、そのような借用されたメソッドはまったく使用されません(ただし、主要な設計セマンティクスに影響します)。
私は、C#スタイルの明示的なコンテキスト/名前空間の表示が役立つことに同意する傾向があります。


1
Android開発にJavaを使い始めるまで、C#がいかに思慮深く機能が豊富であるかを知りませんでした。私はそれらのC#機能を当然のことと思っていました。Javaにはあまりにも多くの機能がありません。
いまいましい野菜

1

私の頭に浮かんだ唯一の解決策は、複数のインターフェースを強制したいオブジェクトへの参照オブジェクトを使用することです。

例:実装するインターフェースが2つあるとします

public interface Framework1Interface {

    void method(Object o);
}

そして

public interface Framework2Interface {
    void method(Object o);
}

それらを2つのFacadorオブジェクトに含めることができます。

public class Facador1 implements Framework1Interface {

    private final ObjectToUse reference;

    public static Framework1Interface Create(ObjectToUse ref) {
        return new Facador1(ref);
    }

    private Facador1(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework1Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork1(o);
    }
}

そして

public class Facador2 implements Framework2Interface {

    private final ObjectToUse reference;

    public static Framework2Interface Create(ObjectToUse ref) {
        return new Facador2(ref);
    }

    private Facador2(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework2Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork2(o);
    }
}

結局、あなたが望むクラスは次のようなものでなければなりません

public class ObjectToUse {

    private Framework1Interface facFramework1Interface;
    private Framework2Interface facFramework2Interface;

    public ObjectToUse() {
    }

    public Framework1Interface getAsFramework1Interface() {
        if (facFramework1Interface == null) {
            facFramework1Interface = Facador1.Create(this);
        }
        return facFramework1Interface;
    }

    public Framework2Interface getAsFramework2Interface() {
        if (facFramework2Interface == null) {
            facFramework2Interface = Facador2.Create(this);
        }
        return facFramework2Interface;
    }

    public void methodForFrameWork1(Object o) {
    }

    public void methodForFrameWork2(Object o) {
    }
}

getAs *メソッドを使用してクラスを「公開」できるようになりました


0

これらを機能させるために、アダプタパターンを使用できます。インターフェイスごとに2つのアダプタを作成し、それを使用します。それは問題を解決するはずです。


-1

問題のすべてのコードを完全に制御でき、これを事前に実装できれば、すべてうまくいきます。ここで、メソッドを使用して多くの場所で使用されている既存のパブリッククラスがあると想像してください。

public class MyClass{

    private String name;

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

次に、WBPInterfaceを実装するためのクラスを必要とする既製のWizzBangProcessorに渡す必要があります...これにはgetName()メソッドもありますが、具体的な実装の代わりに、このインターフェイスはメソッドが型の名前を返すことを期待していますウィズバン処理の。

C#ではそれは些細なことです

public class MyClass : WBPInterface{

    private String name;

    String WBPInterface.getName(){
        return "MyWizzBangProcessor";
    }

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

Java Toughでは、既存のデプロイ済みコードベースで、あるインターフェイスから別のインターフェイスに変換する必要があるすべてのポイントを特定する必要があります。確かに、WizzBangProcessor会社はgetWizzBangProcessName()を使用する必要がありましたが、彼らも開発者です。彼らの文脈では、getNameは問題ありませんでした。実際、Java以外では、他のほとんどのOOベースの言語がこれをサポートしています。Javaは、すべてのインターフェースを同じメソッドNAMEで実装することを強制することはまれです。

他のほとんどの言語には、「この実装されたインターフェイスのこのメソッドのシグネチャに一致するこのクラスのこのメソッドは、その実装です」という命令を喜んで受け取るコンパイラがあります。結局のところ、インターフェースを定義することの全体的なポイントは、定義を実装から抽象化できるようにすることです。(JavaのInterfacesでデフォルトのメソッドを使用することを始めないでください。デフォルトのオーバーライドは言うまでもありません。確かに、ロードカー用に設計されたすべてのコンポーネントは、空飛ぶ車にぶつかって動作するはずです。どちらも車です...車はヨーイングするだけなので、デフォルトのピッチとロールの入力では、衛星ナビゲーションのデフォルト機能は影響を受けないと確信しています。

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