Ich habe eine Frage zur Verwendung von Switch Case für instanceof
Objekt:
Zum Beispiel: Mein Problem kann in Java reproduziert werden:
if(this instanceof A)
doA();
else if(this instanceof B)
doB();
else if(this instanceof C)
doC():
Wie würde es mit implementiert werden switch...case
?
java
switch-statement
instanceof
olidev
quelle
quelle
Antworten:
Dies ist ein typisches Szenario, in dem Subtyp-Polymorphismus hilfreich ist. Mach Folgendes
Dann können Sie rufen Sie einfach
do()
anthis
.Wenn Sie nicht frei
A
sindB
, und zu ändern , könnenC
Sie das Besuchermuster anwenden, um dasselbe zu erreichen.quelle
Wenn Sie absolut nicht für eine Schnittstelle codieren können, können Sie eine Aufzählung als Vermittler verwenden:
quelle
Erstellen Sie einfach eine Map, in der die Klasse der Schlüssel und die Funktionalität, dh Lambda oder ähnliches, der Wert ist.
// Refactor dies natürlich so, dass es nur einmal initialisiert wird
Wenn Sie aktivierte Ausnahmen benötigen, implementieren Sie ein FunctionalInterface, das die Ausnahme auslöst, und verwenden Sie diese anstelle von Runnable.
quelle
instanceof
, und nein, mein Szenario ist leider nicht eines von denen, in die es leicht passt diese Box ...)Nur für den Fall, dass jemand es liest:
Die beste Lösung in Java ist:
Die großen Vorteile eines solchen Musters sind:
Du machst es einfach so (KEINE Schalter überhaupt):
Wenn Sie eine neue Aktion mit dem Namen "d" hinzufügen, MÜSSEN Sie die Methode doAction (...) implementieren
HINWEIS: Dieses Muster ist in Joshuas Bloch "Effective Java (2nd Edition)" beschrieben.
quelle
@Override
über jeder Implementierung von erforderlichdoAction()
?action
? Durch eine äußere Instanz der Kaskade, diesomeFunction()
mit der richtigen aufruftaction
? Dies fügt nur eine weitere Indirektionsebene hinzu.Das kannst du nicht. Die
switch
Anweisung kann nurcase
Anweisungen enthalten , die Kompilierzeitkonstanten sind und eine Ganzzahl ergeben (bis zu Java 6 und eine Zeichenfolge in Java 7).Was Sie suchen, wird in der funktionalen Programmierung als "Pattern Matching" bezeichnet.
Siehe auch Vermeiden von Instanzen in Java
quelle
Wie in den Top-Antworten erläutert, besteht der traditionelle OOP-Ansatz darin, Polymorphismus anstelle von Switch zu verwenden. Es gibt sogar ein gut dokumentiertes Refactoring-Muster für diesen Trick: Ersetzen Sie Bedingt durch Polymorphismus . Wann immer ich nach diesem Ansatz greife, möchte ich auch ein Null-Objekt implementieren , um das Standardverhalten bereitzustellen.
Ab Java 8 können wir Lambdas und Generika verwenden, um uns etwas zu geben, mit dem funktionale Programmierer sehr vertraut sind: Pattern Matching. Es ist keine Kernsprache, aber die Javaslang-Bibliothek bietet eine Implementierung. Beispiel aus dem Javadoc :
Es ist nicht das natürlichste Paradigma in der Java-Welt. Verwenden Sie es daher mit Vorsicht. Mit den generischen Methoden müssen Sie zwar den übereinstimmenden Wert nicht typisieren, es fehlt jedoch eine Standardmethode zum Zerlegen des übereinstimmenden Objekts, wie beispielsweise bei Scalas Fallklassen .
quelle
Ich weiß, dass dies sehr spät ist, aber für zukünftige Leser ...
Beachten Sie die oben genannten Ansätze, die nur auf dem Namen der Klasse von A , B , C ... basieren :
Sofern Sie nicht garantieren können, dass A , B , C ... (alle Unterklassen oder Implementierer von Base ) endgültig sind, werden Unterklassen von A , B , C ... nicht behandelt.
Obwohl der Ansatz if, elseif, elseif .. für eine große Anzahl von Unterklassen / Implementierern langsamer ist, ist er genauer.
quelle
Leider ist dies nicht sofort möglich, da die switch-case-Anweisung einen konstanten Ausdruck erwartet. Um dies zu überwinden, besteht eine Möglichkeit darin, Enum-Werte mit den Klassennamen zu verwenden, z
Damit ist es möglich, die switch-Anweisung so zu verwenden
quelle
Nein, dazu gibt es keine Möglichkeit. Was Sie jedoch tun möchten, ist, Polymorphismus als einen Weg zu betrachten, um diese Art von Problemen zu behandeln.
quelle
Die Verwendung solcher switch-Anweisungen ist nicht objektorientiert. Sie sollten stattdessen die Kraft des Polymorphismus nutzen . Einfach schreiben
Nachdem Sie zuvor eine Basisklasse eingerichtet haben:
Das ist die Basisklasse für
A
,B
undC
:quelle
Dies funktioniert schneller und macht Sinn, falls
- Sie haben relativ viele "Fälle"
- der Prozess in einem leistungsabhängigen Kontext ausgeführt wird
quelle
Sie können nicht, dass ein Switch nur mit den Typen Byte, Short, Char, Int, String und Enumerated funktioniert (und den Objektversionen der Grundelemente, es hängt auch von Ihrer Java-Version ab, Strings können
switch
in Java 7 bearbeitet werden).quelle
Ich persönlich mag den folgenden Java 1.8-Code:
Wird ausgegeben:
Der Beispielcode verwendet Strings, Sie können jedoch jeden Objekttyp verwenden, einschließlich Class. z.B
.myCase(this.getClass(), (o) -> ...
Benötigt das folgende Snippet:
quelle
Mit Java können Sie jetzt in der Art des OP wechseln. Sie nennen es Pattern Matching für Switch. Es befindet sich derzeit im Entwurf, aber da ich sehe, wie viel Arbeit sie in letzter Zeit in Schalter gesteckt haben, denke ich, dass es durchgehen wird. Das im JEP gegebene Beispiel ist
oder verwenden Sie ihre Lambda-Syntax und geben Sie einen Wert zurück
So oder so haben sie coole Sachen mit Schaltern gemacht.
quelle
Wenn Sie die allgemeine Schnittstelle bearbeiten können, können Sie eine Aufzählung hinzufügen und jede Klasse einen eindeutigen Wert zurückgeben lassen. Sie benötigen keine Instanz oder ein Besuchermuster.
Für mich musste die Logik in der switch-Anweisung geschrieben sein, nicht das Objekt selbst. Das war meine Lösung:
ClassA, ClassB, and ClassC implement CommonClass
Schnittstelle:
Aufzählung:
Impl:
Wenn Sie mit Java 7 arbeiten, können Sie Zeichenfolgenwerte für die Aufzählung eingeben, und der Switch-Case-Block funktioniert weiterhin.
quelle
value
Feld ist redundant, wenn Sie nur die Enum-Konstanten unterscheiden möchten - Sie können die Konstanten direkt verwenden (wie Sie es tun).Wie wäre es damit ?
quelle
this.getSimpleName()
Nicht sicher, ob das Poster mit JS verwechselt wird (ja, er verwendet die Konsole, hehe).Ich denke, es gibt Gründe, eine switch-Anweisung zu verwenden. Wenn Sie xText generierten Code verwenden, möglicherweise. Oder eine andere Art von EMF-generierten Klassen.
Gibt eine Zeichenfolge des Klassenimplementierungsnamens zurück. dh: org.eclipse.emf.ecore.util.EcoreUtil
gibt die einfache Darstellung zurück, dh: EcoreUtil
quelle
switch
alscase
Bedingung verwenden, da es kein konstanter Wert istWenn Sie durch den Klassentyp "dieses" Objekts "wechseln" müssen, ist diese Antwort die beste https://stackoverflow.com/a/5579385/2078368
Aber wenn Sie "switch" auf eine andere Variable anwenden müssen. Ich würde eine andere Lösung vorschlagen. Definieren Sie folgende Schnittstelle:
Implementieren Sie diese Schnittstelle in jeder Klasse, die Sie "wechseln" möchten. Beispiel:
Danach können Sie es folgendermaßen verwenden:
Das einzige, was Sie beachten sollten - halten Sie die "Typen" für alle Klassen, die das ClassTypeInterface implementieren, eindeutig. Dies ist kein großes Problem, da bei einer Kreuzung ein Kompilierungsfehler für die Anweisung "switch-case" angezeigt wird.
quelle
TYPE
, können Sie eine Aufzählung verwenden, und die Eindeutigkeit ist garantiert (wie in dieser Antwort beschrieben ). Bei jedem der Ansätze müssen Sie jedoch bei einer Umbenennung an zwei Stellen eine Umgestaltung vornehmen.TYPE = "A"
beim Umbenennen möglicherweise nicht berücksichtigt. Insbesondere wenn es sich außerhalb der entsprechenden Klasse befindet, kann es auch vergessen werden, wenn es manuell ausgeführt wird. IntelliJ findet das Vorkommen des Klassennamens auch in Zeichenfolgen oder Kommentaren, aber das ist nur eine Textsuche (anstatt den Syntaxbaum zu betrachten) und enthält daher falsch positive Ergebnisse.Hier ist eine funktionale Möglichkeit, dies in Java 8 mithilfe von http://www.vavr.io/ zu erreichen.
quelle
Erstelle ein Aufzählung mit Klassennamen .
Suchen Sie den Klassennamen des Objekts. Schreib ein Schalterfall über die Aufzählung.
Hoffe das hilft.
quelle
Das Eclipse Modeling Framework hat eine interessante Idee, die auch die Vererbung berücksichtigt. Das Grundkonzept ist in der Switch- Schnittstelle definiert : Das Umschalten erfolgt durch Aufrufen der doSwitch- Methode.
Was wirklich interessant ist, ist die Implementierung. Für jede Art von Interesse a
Methode muss implementiert werden (wobei eine Standardimplementierung null zurückgibt). Die doSwitch- Implementierung versucht, alle caseXXX- Methoden für das Objekt für alle Typhierarchien aufzurufen . Etwas in den Zeilen von:
Das eigentliche Framework verwendet für jede Klasse eine Ganzzahl-ID, sodass die Logik tatsächlich ein reiner Schalter ist:
Sie können sich eine vollständige Implementierung des ECoreSwitch ansehen , um eine bessere Vorstellung zu erhalten.
quelle
Während es nicht möglich ist, eine switch-Anweisung zu schreiben, ist es möglich, für jeden gegebenen Typ zu einer bestimmten Verarbeitung zu verzweigen. Eine Möglichkeit hierfür ist die Verwendung eines Standard-Doppelversandmechanismus. Ein Beispiel, bei dem wir basierend auf dem Typ "wechseln" möchten, ist der Jersey Exception Mapper, bei dem wir eine Vielzahl von Ausnahmen auf Fehlerantworten abbilden müssen. Während es für diesen speziellen Fall wahrscheinlich einen besseren Weg gibt (dh die Verwendung einer polymorphen Methode, die jede Ausnahme in eine Fehlerantwort übersetzt), ist die Verwendung des Doppelversandmechanismus immer noch nützlich und praktisch.
Wo immer der "Schalter" benötigt wird, können Sie dies wie folgt tun:
quelle
Es gibt eine noch einfachere Möglichkeit, eine Switch-Struktur zu emulieren, die instanceof verwendet. Dazu erstellen Sie einen Codeblock in Ihrer Methode und benennen ihn mit einer Bezeichnung. Dann verwenden Sie if-Strukturen, um die case-Anweisungen zu emulieren. Wenn ein Fall zutrifft, verwenden Sie die Unterbrechung LABEL_NAME, um aus Ihrer provisorischen Schalterstruktur herauszukommen.
quelle
if
...else if
Code des OP?if
...else if
durch "goto" -Anweisungen zu ersetzen , was der falsche Weg ist, den Kontrollfluss in Sprachen wie Java zu implementieren.