
Hi, habrozhiteli! Wir haben ein Buch veröffentlicht, um Kotlin mit der Head First-Technik zu untersuchen, das über die Syntax und Anweisungen zur Lösung spezifischer Probleme hinausgeht. Dieses Buch bietet Ihnen alles, was Sie brauchen - von den Grundlagen der Sprache bis zu fortgeschrittenen Methoden. Und Sie können objektorientierte und funktionale Programmierung üben.
Unter dem Schnitt ist ein Auszug "Datenklassen" dargestellt.
Mit Daten arbeiten
Niemand möchte Zeit verschwenden und wiederholen, was bereits getan wurde. Die meisten Anwendungen verwenden Klassen zum Speichern von Daten. Um die Arbeit zu vereinfachen, schlugen die Entwickler von Kotlin das Konzept einer Datenklasse vor. In diesem Kapitel erfahren Sie, wie Datenklassen Ihnen helfen, eleganteren und prägnanteren Code zu schreiben, von dem Sie vorher nur träumen konnten. Wir werden uns die Hilfsfunktionen von Datenklassen ansehen und lernen, wie ein Datenobjekt in Komponenten zerlegt wird. Gleichzeitig werden wir beschreiben, wie die Standardparameterwerte den Code flexibler machen, und Ihnen Any vorstellen, den Vorfahren aller Oberklassen.
Der Operator == ruft eine Funktion namens equals auf
Wie Sie bereits wissen, kann der Operator == verwendet werden, um die Gleichheit zu überprüfen. Jedes Mal, wenn die Anweisung == ausgeführt wird, wird eine Funktion namens equals aufgerufen. Jedes Objekt enthält eine Gleichheitsfunktion, und die Implementierung dieser Funktion bestimmt das Verhalten des Operators ==.
Standardmäßig prüft die Funktion equals zum Überprüfen der Gleichheit, ob zwei Variablen auf dasselbe Objekt verweisen.
Um zu verstehen, wie es funktioniert, stellen Sie sich zwei Wolf-Variablen mit den Namen w1 und w2 vor. Wenn w1 und w2 Verweise auf ein Wolf-Objekt enthalten, ist das Ergebnis beim Vergleich mit dem Operator == wahr:
Wenn jedoch w1 und w2 Verweise auf verschiedene Wolf-Objekte enthalten, ergibt der Vergleich mit dem Operator == das Ergebnis false, selbst wenn die Objekte dieselben Eigenschaftswerte enthalten.
Wie bereits erwähnt, ist die Funktion equals automatisch in jedem von Ihnen erstellten Objekt enthalten. Aber woher kommt diese Funktion?
gleich erbt von der Oberklasse Any
Jedes Objekt enthält eine Funktion namens equals, da seine Klasse eine Funktion von einer Klasse namens Any erbt. Die Any-Klasse ist der Vorfahr aller Klassen: die resultierende Oberklasse von allem. Jede Klasse, die Sie definieren, ist eine Unterklasse von Any, und Sie müssen im Programm nicht darauf hinweisen. Wenn Sie also einen Klassencode namens myClass schreiben, der folgendermaßen aussieht:
class MyClass { ... }
Der Compiler konvertiert es automatisch in das folgende Formular:
Jede Klasse ist eine Unterklasse von Any und erbt ihr Verhalten. Jede Klasse ist eine Unterklasse von Any, und Sie müssen dies nicht im Programm melden.
Die Bedeutung jeglicher Vererbung
Das Einbeziehen von Any als resultierende Superklasse hat zwei wichtige Vorteile:
- Es stellt sicher, dass jede Klasse gemeinsames Verhalten erbt. Die Klasse Any definiert ein wichtiges Verhalten, von dem der Betrieb des Systems abhängt. Und da jede Klasse eine Unterklasse von Any ist, wird dieses Verhalten von allen von Ihnen erstellten Objekten übernommen. Die Any-Klasse definiert also eine Funktion namens equals. Daher wird diese Funktion automatisch von allen Objekten geerbt.
- Dies bedeutet, dass Polymorphismus mit allen Objekten verwendet werden kann. Jede Klasse ist eine Unterklasse von Any, daher hat jedes Objekt, das Sie erstellen, die Any-Klasse als endgültigen Supertyp. Dies bedeutet, dass Sie eine Funktion mit beliebigen Parametern oder einem beliebigen Rückgabetyp erstellen können, der mit Objekten eines beliebigen Typs funktioniert. Dies bedeutet auch, dass Sie polymorphe Arrays zum Speichern von Objekten eines beliebigen Typs mit Code der folgenden Form erstellen können:
val myArray = arrayOf(Car(), Guitar(), Giraffe())
Der Compiler stellt fest, dass jedes Objekt im Array einen gemeinsamen Prototyp von Any hat, und erstellt daher ein Array vom Typ Array.
Das allgemeine Verhalten der Any-Klasse ist einen genaueren Blick wert.
Allgemeines Verhalten von Any geerbt
Die Any-Klasse definiert mehrere Funktionen, die von jeder Klasse geerbt werden. Hier einige Beispiele für Grundfunktionen und deren Verhalten:
- gleich (any: Any): Boolean
Überprüft, ob zwei Objekte als "gleich" betrachtet werden. Standardmäßig gibt die Funktion true zurück, wenn ein Objekt überprüft wird, oder false - für verschiedene Objekte. Hinter den Kulissen wird die Funktion equals jedes Mal aufgerufen, wenn der Operator == im Programm verwendet wird.
val w1 = Wolf() val w1 = Wolf() val w2 = Wolf() val w2 = w1 println(w1.equals(w2)) println(w1.equals(w2)) false (equals false, true (equals true, w1 w2 w1 w2 .) — , w1 == w2.
- hashCode (): Int
Gibt einen Hash-Code für ein Objekt zurück. Hash-Codes werden häufig von einigen Datenstrukturen verwendet, um Werte effizient zu speichern und abzurufen.
val w = Wolf() println(w.hashCode())
523429237 (Wert des Hash-Codes w)
- toString (): String
Gibt eine String-Nachricht zurück, die das Objekt darstellt. Standardmäßig enthält die Nachricht den Klassennamen und eine Nummer, die uns normalerweise egal ist.
val w = Wolf() println(w.toString())
Wolf @ 1f32e575Standardmäßig prüft die Funktion equals, ob zwei Objekte dasselbe tatsächliche Objekt sind.
Die Funktion equals bestimmt das Verhalten des Operators ==.
Die Any-Klasse bietet eine Standardimplementierung für alle aufgelisteten Funktionen, und diese Implementierungen werden von allen Klassen geerbt. Sie können diese Implementierungen jedoch überschreiben, um das Standardverhalten aller aufgelisteten Funktionen zu ändern.
Einfache Äquivalenzprüfung von zwei Objekten
In einigen Situationen müssen Sie die Implementierung der Funktion equals ändern, um das Verhalten des Operators == zu ändern.
Angenommen, Sie haben eine Rezeptklasse, mit der Sie Objekte zum Speichern von Rezepten erstellen können. In einer solchen Situation werden Sie wahrscheinlich zwei Rezeptobjekte als gleich (oder gleichwertig) betrachten, wenn sie eine Beschreibung desselben Rezepts enthalten. Angenommen, die Rezeptklasse ist mit zwei Eigenschaften definiert - title und isVegetarian:
class Recipe(val title: String, val isVegetarian: Boolean) { }
Der Operator == gibt true zurück, wenn er zum Vergleichen von zwei Rezeptobjekten mit denselben Eigenschaften, title und isVegetarian, verwendet wird:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false)
Obwohl Sie das Verhalten des Operators == ändern können, indem Sie zusätzlichen Code schreiben, um die Funktion equals zu überschreiben, haben Kotlin-Entwickler eine bequemere Lösung bereitgestellt: Sie haben das Konzept einer Datenklasse erstellt. Mal sehen, was diese Klassen sind und wie sie erstellt werden.
Mit der Datenklasse können Sie Datenobjekte erstellen.
Eine Datenklasse ist eine Klasse zum Erstellen von Objekten zum Speichern von Daten. Es enthält Tools, die für die Arbeit mit Daten nützlich sind - beispielsweise eine neue Implementierung der Funktion equals, mit der überprüft wird, ob zwei Datenobjekte dieselben Eigenschaftswerte enthalten. Wenn zwei Objekte dieselben Daten enthalten, können sie als gleich angesehen werden.
Um eine Datenklasse zu definieren, stellen Sie der üblichen Datendefinition das Datenschlüsselwort voran. Der folgende Code konvertiert die zuvor erstellte Rezeptklasse in eine Datenklasse:
data class Recipe(val title: String, val isVegetarian: Boolean) { }
Das Datenpräfix konvertiert eine reguläre Klasse in eine Datenklasse.
So erstellen Sie Objekte basierend auf einer Datenklasse
Datenklassenobjekte werden auf die gleiche Weise wie reguläre Klassenobjekte erstellt: durch Aufrufen des Konstruktors dieser Klasse. Der folgende Code erstellt beispielsweise ein neues Rezeptdatenobjekt und weist es einer neuen Variablen mit dem Namen r1 zu:
val r1 = Recipe("Chicken Bhuna", false)
Datenklassen überschreiben automatisch ihre Gleichheitsfunktionen, um das Verhalten des Operators == zu ändern, sodass die Gleichheit von Objekten basierend auf den Eigenschaftswerten jedes Objekts überprüft wird. Wenn Sie beispielsweise zwei Rezeptobjekte mit denselben Eigenschaftswerten erstellen, ergibt der Vergleich der beiden Objekte mit dem Operator == das Ergebnis true, da dieselben Daten in ihnen gespeichert sind:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false)
r1 und r2 werden als "gleich" betrachtet, da zwei Rezeptobjekte dieselben Daten enthalten.
Zusätzlich zur neuen Implementierung der Funktion equals, die von den Datenklassen der Superklasse Any geerbt wurde
Überschreiben Sie auch die Funktionen hashCode und toString. Mal sehen, wie diese Funktionen implementiert werden.
Klassenobjekte definieren ihr geerbtes Verhalten neu
Um mit Daten arbeiten zu können, benötigt die Datenklasse Objekte. Daher werden automatisch die folgenden Implementierungen für die Funktionen equals, hashCode und toString bereitgestellt, die von der Superklasse Any geerbt wurden:
Die Funktion equals vergleicht Eigenschaftswerte
Beim Definieren einer Datenklasse gibt ihre Gleichheitsfunktion (und damit der Operator ==) weiterhin true zurück, wenn die Verknüpfungen auf dasselbe Objekt verweisen. Es wird aber auch true zurückgegeben, wenn die Objekte dieselben im Konstruktor definierten Eigenschaftswerte haben:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false) println(r1.equals(r2)) true
Datenobjekte gelten als gleich, wenn ihre Eigenschaften denselben Wert enthalten.
Für gleiche Objekte werden dieselben hashCode-Werte zurückgegeben
Wenn zwei Datenobjekte als gleich betrachtet werden (mit anderen Worten, sie haben dieselben Eigenschaftswerte), gibt die Funktion hashCode für diese Objekte denselben Wert zurück:
val r1 = Recipe("Chicken Bhuna", false) val r2 = Recipe("Chicken Bhuna", false) println(r1.hashCode()) println(r2.hashCode())
241131113
241131113toString gibt die Werte aller Eigenschaften zurück
Schließlich gibt die Funktion toString nicht mehr den Namen der Klasse gefolgt von einer Zahl zurück, sondern eine nützliche Zeichenfolge mit den Werten aller im Konstruktor der Datenklasse definierten Eigenschaften:
val r1 = Recipe("Chicken Bhuna", false) println(r1.toString()) Recipe(title=Chicken Bhuna, isVegetarian=false)
Zusätzlich zum Überschreiben von Funktionen, die von der Superklasse Any geerbt wurden, bietet die Datenklasse zusätzliche Tools, die eine effizientere Arbeit mit Daten ermöglichen, z. B. die Möglichkeit, Datenobjekte zu kopieren. Mal sehen, wie diese Tools funktionieren.
Kopieren von Datenobjekten mit der Kopierfunktion
Wenn Sie eine Kopie des Datenobjekts erstellen müssen, indem Sie einige seiner Eigenschaften ändern, die anderen Eigenschaften jedoch im ursprünglichen Zustand belassen, verwenden Sie die Kopierfunktion. Dazu wird die Funktion für das Objekt aufgerufen, das Sie kopieren möchten, und die Namen aller veränderlichen Eigenschaften mit neuen Werten werden an das Objekt übergeben.
Angenommen, Sie haben ein Rezeptobjekt mit dem Namen r1, das im Code wie folgt definiert ist:
val r1 = Recipe("Thai Curry", false)
Wenn Sie eine Kopie des Rezeptobjekts erstellen und den Wert der Eigenschaft isVegetarian durch true ersetzen möchten, gehen Sie folgendermaßen vor:
Im Wesentlichen bedeutet dies: "Erstellen Sie eine Kopie des r1-Objekts, ändern Sie den Wert seiner isVegetarian-Eigenschaft in true und weisen Sie einer Variablen mit dem Namen r2 ein neues Objekt zu." Dadurch wird eine neue Kopie des Objekts erstellt, und das ursprüngliche Objekt bleibt unverändert.
Neben der Kopierfunktion bieten Datenklassen auch eine Reihe von Funktionen zum Aufteilen eines Datenobjekts in eine Reihe von Werten seiner Eigenschaften - dieser Vorgang wird als Destrukturierung bezeichnet. Mal sehen, wie das gemacht wird.
Datenklassen definieren KomponentenN ... -Funktionen
Beim Definieren einer Datenklasse fügt der Compiler der Klasse automatisch eine Reihe von Funktionen hinzu, die als alternativer Mechanismus für den Zugriff auf Objekteigenschaftswerte verwendet werden können. Diese Funktionen sind unter dem allgemeinen Namen der Komponenten-N-Funktionen bekannt, wobei N die Anzahl der abzurufenden Eigenschaften ist (in der Deklarationsreihenfolge).
Angenommen, Sie haben das folgende Rezeptobjekt, um zu sehen, wie die Funktionen von componentN funktionieren:
val r = Recipe("Chicken Bhuna", false)
Wenn Sie den Wert der ersten Eigenschaft des Objekts (title-Eigenschaft) abrufen möchten, können Sie dazu die component1 () -Funktion des Objekts aufrufen:
val title = r.component1()
component1 () gibt die Referenz zurück, die in der ersten im Konstruktor der Datenklasse definierten Eigenschaft enthalten ist.
Die Funktion funktioniert wie der folgende Code:
val title = r.title
Der Code mit der Funktion ist universeller. Warum sind die ComponentN-Funktionen in Datenklassen so nützlich?
... zur Umstrukturierung von Datenobjekten
Die generischen componentN-Funktionen sind vor allem deshalb nützlich, weil sie eine einfache und bequeme Möglichkeit bieten, ein Datenobjekt in Eigenschaftswerte aufzuteilen oder zu zerstören.
Angenommen, Sie möchten die Werte der Eigenschaften eines Rezeptobjekts übernehmen und den Wert jeder seiner Eigenschaften einer separaten Variablen zuweisen. Anstelle von Code
val title = r.title val vegetarian = r.isVegetarian
Bei der sequentiellen Verarbeitung jeder Eigenschaft können Sie den folgenden Code verwenden:
val (title, vegetarian) = r
Weist der ersten Eigenschaft r den Titel und der zweiten Eigenschaft den vegetarischen Titel zu.
Dieser Code bedeutet "Erstellen Sie zwei Variablen, Titel und Vegetarisch, und weisen Sie den Wert einer der r Eigenschaften jeder Variablen zu." Er macht dasselbe wie das nächste Fragment
val title = r.component1() val vegetarian = r.component2()
aber es fällt kompakter aus.
Der Operator === prüft immer, ob sich zwei Variablen auf dasselbe Objekt beziehen.Wenn Sie überprüfen möchten, ob zwei Variablen unabhängig von ihrem Typ auf dasselbe Objekt verweisen, verwenden Sie den Operator === anstelle von ==. Der Operator === gibt das Ergebnis nur dann als wahr an, wenn (und nur wenn) zwei Variablen einen Verweis auf ein tatsächliches Objekt enthalten. Wenn Sie zwei Variablen haben, x und y, und den folgenden Ausdruck:
x === y
Wenn das Ergebnis true ist, wissen Sie, dass sich die Variablen x und y auf dasselbe Objekt beziehen müssen.
Im Gegensatz zum Operator == ist das Verhalten des Operators === unabhängig von der Funktion equals. Der Operator === verhält sich unabhängig vom Klassentyp immer gleich.
Nachdem Sie gelernt haben, wie Sie Datenklassen erstellen und verwenden, erstellen Sie ein Projekt für den Rezeptcode.
Erstellen eines Rezeptprojekts
Erstellen Sie ein neues Kotlin-Projekt für die JVM und nennen Sie es "Rezepte". Dann erstellen Sie eine neue
Kotlin-Datei mit dem Namen Recipes.kt: Wählen Sie den Ordner src aus, öffnen Sie das Menü Datei und wählen Sie den Befehl aus
Neu → Kotlin-Datei / Klasse. Geben Sie den Dateinamen "Rezepte" ein und wählen Sie die Option "Datei" in der Gruppe "Art".
Wir fügen dem Projekt eine neue Datenklasse mit dem Namen Rezept hinzu und erstellen Rezeptdatenobjekte. Unten ist der Code. Aktualisieren Sie Ihre Version von Recipes.kt und bringen Sie sie mit unserer in Einklang:
data class Recipe(val title: String, val isVegetarian: Boolean) ( {} , .) fun main(args: Array<String>) { val r1 = Recipe("Thai Curry", false) val r2 = Recipe("Thai Curry", false) val r3 = r1.copy(title = "Chicken Bhuna") ( r1 title) println("r1 hash code: ${r1.hashCode()}") println("r2 hash code: ${r2.hashCode()}") println("r3 hash code: ${r3.hashCode()}") println("r1 toString: ${r1.toString()}") println("r1 == r2? ${r1 == r2}") println("r1 === r2? ${r1 === r2}") println("r1 == r3? ${r1 == r3}") val (title, vegetarian) = r1 ( r1) println("title is $title and vegetarian is $vegetarian") }
Wenn Sie Ihren Code ausführen, wird der folgende Text im Ausgabefenster der IDE angezeigt:
r1 hash code: -135497891 r2 hash code: -135497891 r3 hash code: 241131113 r1 toString: Recipe(title=Thai Curry, isVegetarian=false) r1 == r2? true r1 === r2? false r1 == r3? false title is Thai Curry and vegetarian is false
»Weitere Informationen zum Buch finden Sie auf
der Website des Herausgebers»
Inhalt»
Auszug
25% Rabatt auf Gutschein für Khabrozhitel -
KotlinNach Bezahlung der Papierversion des Buches wird ein elektronisches Buch per E-Mail verschickt.