Wenn ich zwei Schnittstellen habe, die beide in ihren Zwecken sehr unterschiedlich sind, aber dieselbe Methodensignatur haben, wie kann ich eine Klasse dazu bringen, beide zu implementieren, ohne gezwungen zu sein, eine einzige Methode zu schreiben, die für beide Schnittstellen dient, und eine verschlungene Logik in die Methode zu schreiben Implementierung, die prüft, für welchen Objekttyp der Aufruf ausgeführt wird, und den richtigen Code aufruft?
In C # wird dies durch die sogenannte explizite Schnittstellenimplementierung überwunden. Gibt es einen gleichwertigen Weg in Java?
Antworten:
Nein, es gibt keine Möglichkeit, dieselbe Methode in einer Klasse in Java auf zwei verschiedene Arten zu implementieren.
Das kann zu vielen verwirrenden Situationen führen, weshalb Java dies nicht zugelassen hat.
Sie können eine Klasse aus zwei Klassen zusammenstellen, die jeweils eine andere Schnittstelle implementieren. Dann hat diese eine Klasse das Verhalten beider Schnittstellen.
quelle
ISomething1 CompositeClass.asInterface1();
undISomething2 CompositeClass.asInterface2();
Verfahren zu dieser Klasse. Dann können Sie einfach das eine oder andere aus der zusammengesetzten Klasse herausholen. Es gibt jedoch keine gute Lösung für dieses Problem.public long getCountAsLong() implements interface2.getCount {...}
[falls die Schnittstelle eine erfordert,long
aber Benutzer der Klasse erwartenint
] oderprivate void AddStub(T newObj) implements coolectionInterface.Add
[vorausgesetzt, escollectionInterface
gibt einecanAdd()
Methode und für alle Instanzen dieser Klasse wird sie zurückgegebenfalse
]?Es gibt keinen wirklichen Weg, dies in Java zu lösen. Sie können innere Klassen als Problemumgehung verwenden:
Obwohl es keine Casts von
AlfaBeta
bis zulässtBeta
, sind Downcasts im Allgemeinen böse, und wenn zu erwarten ist, dass eineAlfa
Instanz häufig auch einenBeta
Aspekt hat, möchten Sie aus irgendeinem Grund (normalerweise ist Optimierung der einzig gültige Grund) in der Lage sein zu konvertieren esBeta
, könnten Sie eine Unter Schnittstelle von machenAlfa
mitBeta asBeta()
drin.quelle
Wenn Sie auf dieses Problem stoßen, liegt dies höchstwahrscheinlich daran, dass Sie die Vererbung dort verwenden, wo Sie die Delegierung verwenden sollten . Wenn Sie zwei verschiedene, wenn auch ähnliche Schnittstellen für dasselbe zugrunde liegende Datenmodell bereitstellen müssen , sollten Sie eine Ansicht verwenden , um über eine andere Schnittstelle kostengünstig Zugriff auf die Daten zu erhalten.
Um ein konkretes Beispiel für den letzteren Fall zu geben, nehmen wir an, Sie möchten beide
Collection
und implementierenMyCollection
(die nicht vonCollection
einer inkompatiblen Schnittstelle erben und diese haben). Sie könnten einCollection getCollectionView()
undMyCollection getMyCollectionView()
Funktionen bereitstellen, die eine leichte Implementierung vonCollection
und ermöglichenMyCollection
unter Verwendung derselben zugrunde liegenden Daten ermöglichen.Für den ersteren Fall ... nehmen wir an, Sie möchten wirklich ein Array von Ganzzahlen und ein Array von Zeichenfolgen. Anstatt von beiden
List<Integer>
und zu erbenList<String>
, sollten Sie ein Mitglied vom TypList<Integer>
und ein anderes Mitglied vom TypList<String>
haben und auf diese Mitglieder 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 anstelle von Vererbung zu verwenden.quelle
Das "klassische" Java-Problem betrifft auch meine Android-Entwicklung ...
Der Grund scheint einfach zu sein:
Mehr Frameworks / Bibliotheken, die Sie verwenden müssen, leichter können Dinge außer Kontrolle geraten ...
In meinem Fall habe ich eine BootStrapperApp- Klasse von android.app.Application geerbt ,
während dieselbe Klasse auch eine Plattformschnittstelle eines MVVM-Frameworks implementieren sollte , um integriert zu werden.
Eine Methodenkollision trat bei einer getString () -Methode auf, die von beiden Schnittstellen angekündigt wird und in verschiedenen Kontexten eine unterschiedliche Implementierung aufweisen sollte.
Die Problemumgehung (hässlich ... IMO) verwendet eine innere Klasse, um alle Plattformen zu implementierenMethoden, nur aufgrund eines geringfügigen Methodensignaturkonflikts ... In einigen Fällen wird eine solche geliehene Methode überhaupt nicht verwendet (hat jedoch die Hauptentwurfs-Semantik beeinflusst).
Ich stimme eher zu, dass eine explizite Kontext- / Namespace-Angabe im C # -Stil hilfreich ist.
quelle
Die einzige Lösung, die mir in den Sinn kam, ist die Verwendung von Referenzobjekten für das Objekt, für das Sie mehrere Schnittstellen implementieren möchten.
Beispiel: Angenommen, Sie müssen 2 Schnittstellen implementieren
und
Sie können sie in zwei Facador-Objekte einschließen:
und
Am Ende sollte die Klasse, die Sie wollten, so etwas wie
Sie können jetzt die getAs * -Methoden verwenden, um Ihre Klasse "verfügbar zu machen"
quelle
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.
quelle
Alles schön und gut, wenn Sie die vollständige Kontrolle über den gesamten fraglichen Code haben und dies im Voraus implementieren können. Stellen Sie sich nun vor, Sie haben eine vorhandene öffentliche Klasse, die an vielen Stellen mit einer Methode verwendet wird
Jetzt müssen Sie es an den Standard-WizzBangProcessor übergeben, für den Klassen erforderlich sind, um das WBPInterface zu implementieren ... das ebenfalls eine getName () -Methode enthält. Anstelle Ihrer konkreten Implementierung erwartet diese Schnittstelle jedoch, dass die Methode den Namen eines Typs zurückgibt von Wizz Bang Processing.
In C # wäre es ein Trvial
In Java Tough müssen Sie jeden Punkt in der vorhandenen bereitgestellten Codebasis identifizieren, an dem Sie von einer Schnittstelle zur anderen konvertieren müssen. Sicher, die Firma WizzBangProcessor hätte getWizzBangProcessName () verwenden sollen, aber sie sind auch Entwickler. In ihrem Kontext war getName in Ordnung. Außerhalb von Java unterstützen dies die meisten anderen OO-basierten Sprachen. Java erzwingt selten, dass alle Schnittstellen mit derselben Methode NAME implementiert werden.
Die meisten anderen Sprachen haben einen Compiler, der gerne eine Anweisung entgegennimmt, um zu sagen: "Diese Methode in dieser Klasse, die mit der Signatur dieser Methode in dieser implementierten Schnittstelle übereinstimmt, ist ihre Implementierung." Schließlich geht es beim Definieren von Schnittstellen darum, dass die Definition von der Implementierung abstrahiert wird. (Lassen Sie mich nicht einmal anfangen, Standardmethoden in Interfaces in Java zu verwenden, geschweige denn Standardmethoden zu überschreiben ... denn sicher sollte jede für ein Straßenauto entwickelte Komponente in der Lage sein, in ein fliegendes Auto eingeschlagen zu werden und einfach zu arbeiten - hey Sie sind beide Autos ... Ich bin sicher, dass die Standardfunktionalität Ihres Navigationsgeräts nicht durch Standard-Pitch- und Roll-Eingaben beeinflusst wird, da Autos nur gieren!
quelle