Wenn Sie Code schreiben, der viele schöne, unveränderliche Datenstrukturen verwendet, scheinen Fallklassen ein Glücksfall zu sein, sodass Sie mit nur einem Schlüsselwort kostenlos Folgendes erhalten:
- Alles standardmäßig unveränderlich
- Getter werden automatisch definiert
- Ordentliche toString () Implementierung
- Konform mit equals () und hashCode ()
- Begleitobjekt mit der Methode unapply () zum Abgleichen
Aber was sind die Nachteile der Definition einer unveränderlichen Datenstruktur als Fallklasse?
Welche Einschränkungen gibt es für die Klasse oder ihre Kunden?
Gibt es Situationen, in denen Sie eine Nicht-Fall-Klasse bevorzugen sollten?
scala
case-class
Graham Lea
quelle
quelle
Antworten:
Ein großer Nachteil: Eine Fallklasse kann eine Fallklasse nicht erweitern. Das ist die Einschränkung.
Weitere Vorteile, die Sie der Vollständigkeit halber verpasst haben: konforme Serialisierung / Deserialisierung, zum Erstellen muss kein "neues" Schlüsselwort verwendet werden.
Ich bevorzuge Nicht-Fall-Klassen für Objekte mit veränderlichem Status, privatem Status oder keinem Status (z. B. die meisten Singleton-Komponenten). Fallklassen für so ziemlich alles andere.
quelle
Zuerst die guten Teile:
Alles standardmäßig unveränderlich
Ja, und kann
var
bei Bedarf sogar überschrieben werdenGetter werden automatisch definiert
Möglich in jeder Klasse durch Präfixieren von Parametern mit
val
Ordentliche
toString()
UmsetzungJa, sehr nützlich, aber bei Bedarf in jeder Klasse von Hand machbar
Konform
equals()
undhashCode()
In Kombination mit dem einfachen Mustervergleich ist dies der Hauptgrund, warum Benutzer Fallklassen verwenden
Begleitobjekt mit
unapply()
Methode zum AbgleichenEs ist auch möglich, jede Klasse mit Extraktoren von Hand zu bearbeiten
Diese Liste sollte auch die überaus leistungsstarke Kopiermethode enthalten, eine der besten Voraussetzungen für Scala 2.8
Dann das Schlechte, es gibt nur eine Handvoll wirklicher Einschränkungen bei Fallklassen:
Sie können
apply
im Begleitobjekt nicht dieselbe Signatur wie die vom Compiler generierte Methode definierenIn der Praxis ist dies jedoch selten ein Problem. Das Ändern des Verhaltens der generierten Apply-Methode wird die Benutzer garantiert überraschen und sollte dringend entmutigt werden. Die einzige Rechtfertigung dafür ist die Validierung von Eingabeparametern - eine Aufgabe, die am besten im Hauptkonstruktorkörper ausgeführt wird (wodurch die Validierung auch bei Verwendung verfügbar wird
copy
).Sie können keine Unterklasse
Es stimmt, obwohl es immer noch möglich ist, dass eine Fallklasse selbst ein Nachkomme ist. Ein gängiges Muster besteht darin, eine Klassenhierarchie von Merkmalen aufzubauen, wobei Fallklassen als Blattknoten des Baums verwendet werden.
Es ist auch erwähnenswert, den
sealed
Modifikator. Jede Unterklasse eines Merkmals mit diesem Modifikator muss in derselben Datei deklariert werden. Beim Mustervergleich mit Instanzen des Merkmals kann der Compiler Sie warnen, wenn Sie nicht alle möglichen konkreten Unterklassen überprüft haben. In Kombination mit Fallklassen kann dies ein hohes Maß an Vertrauen in Ihren Code bieten, wenn dieser ohne Vorwarnung kompiliert wird.Als Unterklasse von Product können Fallklassen nicht mehr als 22 Parameter haben
Keine wirkliche Problemumgehung, außer um den Missbrauch von Klassen mit so vielen Parametern zu stoppen :)
Ebenfalls...
Eine andere Einschränkung, die manchmal bemerkt wird, ist, dass Scala (derzeit) keine faulen Parameter unterstützt (wie
lazy val
s, sondern als Parameter). Die Problemumgehung besteht darin, einen By-Name-Parameter zu verwenden und ihn im Konstruktor einem Lazy Val zuzuweisen. Leider mischen sich namentliche Parameter nicht mit dem Mustervergleich, wodurch verhindert wird, dass die Technik mit Fallklassen verwendet wird, da der vom Compiler generierte Extraktor beschädigt wird.Dies ist relevant, wenn Sie hochfunktionale Lazy-Data-Strukturen implementieren möchten, und wird hoffentlich durch Hinzufügen von Lazy-Parametern zu einer zukünftigen Version von Scala behoben.
quelle
Ich denke, hier gilt das TDD-Prinzip: Überdesign nicht. Wenn Sie etwas als a
case class
deklarieren, deklarieren Sie viele Funktionen. Dies verringert die Flexibilität, die Sie beim zukünftigen Wechsel der Klasse haben.Zum Beispiel
case class
hat a eineequals
Methode über die Konstruktorparameter. Das interessiert Sie vielleicht nicht, wenn Sie Ihre Klasse zum ersten Mal schreiben, aber letztere entscheiden möglicherweise, dass die Gleichheit einige dieser Parameter ignoriert oder etwas anderes tut. Client-Code kann jedoch in der Zwischenzeit geschrieben werden, die von dercase class
Gleichheit abhängt .quelle
Martin Odersky gibt uns einen guten Ausgangspunkt in seinem Kurs Funktionsprogrammierprinzipien in Scala (Vorlesung 4.6 - Pattern Matching), den wir verwenden können, wenn wir zwischen Klasse und Fallklasse wählen müssen. Das Kapitel 7 von Scala By Example enthält dasselbe Beispiel.
Darüber hinaus führt das Hinzufügen einer neuen Prod-Klasse zu keinen Änderungen am vorhandenen Code:
Im Gegensatz dazu erfordert das Hinzufügen einer neuen Methode die Änderung aller vorhandenen Klassen.
Das gleiche Problem wurde mit Fallklassen gelöst.
Das Hinzufügen einer neuen Methode ist eine lokale Änderung.
Das Hinzufügen einer neuen Prod-Klasse erfordert möglicherweise das Ändern aller Musterübereinstimmungen.
Transkript aus der Videolecture 4.6 Pattern Matching
Denken Sie daran: Wir müssen dies als Ausgangspunkt verwenden und nicht als die einzigen Kriterien.
quelle
Ich zitiere diese aus
Scala cookbook
nachAlvin Alexander
Kapitel 6:objects
.Dies ist eines der vielen Dinge, die ich in diesem Buch interessant fand.
Um mehrere Konstruktoren für eine Fallklasse bereitzustellen, ist es wichtig zu wissen, was die Fallklassendeklaration tatsächlich bewirkt.
Wenn Sie sich den Code ansehen, den der Scala-Compiler für das Fallklassenbeispiel generiert, werden Sie feststellen, dass zwei Ausgabedateien erstellt werden, Person $ .class und Person.class. Wenn Sie Person $ .class mit dem Befehl javap zerlegen, werden Sie feststellen, dass es neben vielen anderen eine Methode zum Anwenden enthält:
Sie können Person.class auch zerlegen, um zu sehen, was es enthält. Für eine einfache Klasse wie diese enthält sie weitere 20 Methoden. Dieses versteckte Aufblähen ist einer der Gründe, warum manche Entwickler Fallklassen nicht mögen.
quelle