it-swarm.com.de

Java - Methodennamenkollision bei der Schnittstellenimplementierung

Wenn ich zwei Schnittstellen habe, die sich in ihren Zwecken ziemlich voneinander unterscheiden, aber mit derselben Methodensignatur, wie kann ich eine Klasse beide implementieren lassen, ohne dazu gezwungen zu sein, eine einzige Methode zu schreiben, die für die beiden Schnittstellen dient, und eine gewundene Logik in die Methode schreiben Implementierung, die prüft, für welchen Objekttyp der Aufruf ausgeführt wird, und den richtigen Code aufrufen?

In C # wird dies durch eine explizite Schnittstellenimplementierung überwunden. Gibt es einen gleichwertigen Weg in Java?

85
Bhaskar

Nein, es gibt keine Möglichkeit, dieselbe Methode auf zwei verschiedene Arten in einer Klasse in Java zu implementieren.

Dies kann zu vielen verwirrenden Situationen führen, weshalb Java dies nicht zulässt.

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

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

Sie können eine Klasse aus zwei Klassen zusammenstellen, die jeweils eine andere Schnittstelle implementieren. Dann hat diese eine Klasse das Verhalten beider Schnittstellen.

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

Es gibt keinen wirklichen Weg, dies in Java zu lösen. Sie können innere Klassen als Workaround verwenden:

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

Obwohl Casts von AlfaBeta auf Beta nicht zulässig sind, sind Downcasts im Allgemeinen böse, und wenn zu erwarten ist, dass eine Alfa-Instanz häufig auch einen Beta-Aspekt hat, und zwar aus irgendeinem Grund (normalerweise ist die Optimierung der einzige Grund) Um es in Beta konvertieren zu können, können Sie ein Unter-Interface von Alfa mit Beta asBeta() darin erstellen.

13
gustafc

Wenn dieses Problem auftritt, liegt dies höchstwahrscheinlich daran, dass Sie es verwenden erbe wo solltest du sein delegation. Wenn Sie zwei verschiedene, wenn auch ähnliche Schnittstellen für dasselbe zugrunde liegende Datenmodell bereitstellen müssen, sollten Sie a verwenden aussicht um kostengünstig den Zugriff auf die Daten über eine andere Schnittstelle zu ermöglichen.

Um ein konkretes Beispiel für den letzteren Fall zu geben, nehmen Sie an, Sie möchten sowohl Collection als auch MyCollection implementieren (das nicht von Collection erbt und eine inkompatible Schnittstelle hat). Sie können eine Collection getCollectionView()- und eine MyCollection getMyCollectionView()-Funktion bereitstellen, die eine leichtgewichtige Implementierung von Collection und MyCollection mit den gleichen zugrunde liegenden Daten bereitstellt. 

Für den ersten Fall ... nehmen Sie an, Sie möchten wirklich ein Array von Ganzzahlen und ein String-Array. Statt von List<Integer> und List<String> zu erben, sollten Sie ein Element vom Typ List<Integer> und ein anderes Mitglied vom Typ List<String> haben und auf diese Elemente verweisen, anstatt zu versuchen, von beiden zu erben. Selbst wenn Sie nur eine Liste von Ganzzahlen benötigen, ist es in diesem Fall besser, Komposition/Delegierung über Vererbung zu verwenden.

11

Das "klassische" Java-Problem betrifft auch meine Android-Entwicklung ...
Der Grund scheint einfach zu sein: 
Weitere Frameworks/Bibliotheken, die Sie verwenden müssen, können leichter außer Kontrolle geraten ...

In meinem Fall habe ich eineBootStrapperAppKlasse, die vonAndroid.app.Applicationgeerbt wurde.
Während dieselbe Klasse auch einePlatform- Schnittstelle eines MVVM-Frameworks implementieren sollte, um integriert zu werden.
Eine Methodenkollision ist bei einergetString ()- Methode aufgetreten, die von beiden Schnittstellen angekündigt wird und eine unterschiedliche Implementierung in verschiedenen Kontexten haben sollte.
Die Problemumgehung (hässlich..IMO) verwendet eine innere Klasse, um alle Platform -Methoden zu implementieren, nur aufgrund eines geringfügigen Methodenkonflikts ... In einigen Fällen wird eine solche ausgeliehene Methode überhaupt nicht verwendet (aber betroffene Design-Semantik).
Ich stimme eher zu, dass die Angabe des expliziten Kontext-/Namensraums im C # -Stil hilfreich ist.

1
tiancheng

Die einzige Lösung, die mir in den Sinn kam, ist die Verwendung von Referenzobjekten für die, die Sie mit mehreren Schnittstellen implementieren möchten.

beispiel: Angenommen, Sie haben 2 Schnittstellen, die implementiert werden sollen

public interface Framework1Interface {

    void method(Object o);
}

und 

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

sie können sie in zwei Facador-Objekten einschließen:

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

und

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

Am Ende sollte der Unterricht, den Sie wollten, etwas ähneln

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

sie können jetzt die getAs * -Methoden verwenden, um Ihre Klasse "freizulegen" 

1
notAtAll

Sie können ein Adaptermuster verwenden, damit diese funktionieren. Erstellen Sie zwei Adapter für jede Schnittstelle und verwenden Sie diese. Es sollte das Problem lösen.

0
AlexCon