Eine Datenklasse ist ein Konzept, das nicht an eine bestimmte Programmiersprache gebunden ist, sondern ein Muster, das für die meisten Programmierer praktisch genug ist, um Informationen auf einfache Weise darzustellen, zu kapseln und zu verschieben.

Eine Datenklasse bezieht sich auf eine Klasse, die nur Felder und Crud-Methoden für den Zugriff darauf enthält (Getter und Setter). Dies sind einfach Container für Daten, die von anderen Klassen verwendet werden. Diese Klassen enthalten keine zusätzlichen Funktionen und können nicht unabhängig von den Daten arbeiten, die sie besitzen.

Normalerweise stellen Datenklassen reale Entitäten dar, und es ist üblich, Dutzende oder Hunderte dieser Klassen in einem Projekt zu haben.

Eine Datenklasse in Java

So sieht eine Datenklasse normalerweise in Java aus:

Sie werden feststellen, dass wir die Methoden toString() , equals() und hashCode() (in der Klasse Object.java deklariert) überschreiben. Warum ist das Überschreiben dieser Methoden in Datenklassen relevant?

Bei der Implementierung dieser drei Methoden erstellen wir Wertobjekte, Klassen, für die zwei beliebige Instanzen mit entsprechend gleichen Feldwerten als austauschbar betrachtet werden. (Hinweis: Um vollständig wahr zu sein, müssen wir die Klasse unveränderlich machen. Es hilft, die Felder endgültig zu machen und die Setter zu entfernen. Unveränderlichkeit wird oft als gute Praxis angesehen und wenn möglich empfohlen)

  • equals() : Standardmäßig wird true nur zurückgegeben, wenn sich die beiden Variablen auf dasselbe Objekt beziehen, dh wenn die Position im Speicher dieselbe ist. Wir überschreiben diese Methode für die Rückgabe von true, wenn die Objekte dieselben Informationen enthalten, dh wenn die Objekte dieselbe Entität darstellen. Es überprüft, ob die Eigenschaften identisch sind und denselben Wert enthalten.
  • hashCode() : Standardmäßig wird die Speicheradresse des Objekts hexadezimal zurückgegeben. Es ist ein numerischer Wert, um ein Objekt während des Gleichheitstests zu identifizieren, dh gleiche Objekte haben denselben Hash-Code. Diese Methode muss überschrieben werden, wenn toString() überschrieben wird, damit ein Hash-Code zurückgegeben wird, der aus den Werten der Eigenschaften berechnet wird.
  • toString(): Standardmäßig gibt den Objekttyp und die hashCode(), zum Beispiel [email protected]+ . Wir überschreiben diese Methode, um eine besser lesbare Version des Objekts wie User(name=Steve, surname=Jobs) .

Obwohl es sich um ein so wichtiges Konzept handelt, gibt es im obigen Java-Code nichts, was diese Klasse von anderen unterscheidet. Programmierer können es aufgrund der Klassenstruktur und der Muster als Datenklasse erkennen, aber aus Sicht des Compilers ist diese Klasse nur eine andere Klasse.

Das Erstellen von Datenklassen ist so verbreitet, dass Entwickler häufig die IDE und andere Plugins verwenden, um ihnen bei dieser sich wiederholenden Aufgabe zu helfen. Der Schmerz beim Erstellen einer Datenklasse in Java kann durch Plugins oder die IDE gelindert werden, aber die meisten Fehler werden bei weiteren Änderungen dieser Klassen verursacht. Es ist sehr leicht zu vergessen, alle Begleitmethoden jedes Mal entsprechend zu ändern, wenn ein Feld entfernt oder hinzugefügt wird.

Einer Datenklasse in Java fehlt die Sprachunterstützung. Es ist eine sich wiederholende und fehleranfällige Aufgabe, die für eine moderne Programmiersprache zu viel Reibung darstellt.

Eine Datenklasse in Kotlin

Die gleiche Datenklasse in Kotlin würde ungefähr so aussehen:

data class User(var name: String, var age: Int)

Kotlin erhebt Datenklassen zu Bürgern erster Klasse, indem es das Schlüsselwort data einführt. Lass es uns brechen.

  • Getter und Setter

Die Getter und Setter werden in Kotlin automatisch erstellt, wenn wir Eigenschaften deklarieren. Kurz gesagt, was var name: String bedeutet, ist, dass die User -Klasse eine Eigenschaft hat, die öffentlich ist (Standardsichtbarkeit in Kotlin), veränderbar (var ) und ein String -Typ ist. Da dies öffentlich ist, wird der Getter erstellt, und da dies veränderbar ist, wird der Setter erstellt.

Wenn wir die Klasse schreibgeschützt machen wollen (keine Setter), müssen wir val :

data class User(val name: String, val age: Int)

Wir können val und var in derselben Klassendeklaration mischen. Sie können sich val als final Variable in Java vorstellen.

Alles, was bisher erklärt wurde, ist für jede Klassendeklaration in Kotlin üblich, aber das Schlüsselwort data macht hier einen Unterschied.

  • Das data Schlüsselwort

Das Deklarieren einer Klasse als Datenklasse wird uns toString() , hashCode() und equals() automatisch auf die gleiche Weise implementieren, wie wir es oben für die Java-Klasse beschrieben haben. Wenn wir also eine Benutzerklasse wie User("Steve Jobs",56) erstellen und die toString() -Methode aufrufen, erhalten wir Folgendes:User(name=Steve Jobs, age=56) .

Destrukturierungsdeklarationen

Das Schlüsselwort data stellt Funktionen bereit, die Destrukturierungsdeklarationen ermöglichen. Kurz gesagt, es erstellt eine Funktion für jede Eigenschaft, damit wir solche Dinge tun können:

Copy function

Das Schlüsselwort data gibt uns eine praktische Möglichkeit, Klassen zu kopieren und den Wert einiger Eigenschaften zu ändern. Wenn wir eine Kopie eines Benutzers erstellen möchten, der das Alter ändert, würden wir dies so tun:

Im Klassentext deklarierte Eigenschaften werden ignoriert

Der Compiler verwendet nur die im primären Konstruktor definierten Eigenschaften für die automatisch generierten Funktionen.

Die Eigenschaft address Es wird nicht vom Schlüsselwort data behandelt, was bedeutet, dass die automatisch generierten Implementierungen es ignorieren.

Pair und Triple

Pair und Triple sind Standarddatenklassen in der Bibliothek, aber die Kotin-Dokumente selbst raten von deren Verwendung zugunsten besser lesbarer und maßgeschneiderter Datenklassen ab.

Anforderungen und Einschränkungen

  • Ein Datenklassenkonstruktor muss mindestens einen Parameter haben.
  • Alle Parameter müssen als val oder var angegeben werden.
  • Eine Datenklasse kann nicht abstract, open, sealed oder inner .
  • equals , toString und hashCode Methoden können explizit überschrieben werden.
  • Explizite Implementierungen für componentN() und copy() Funktionen sind nicht zulässig.
  • Das Ableiten einer Datenklasse von einem Typ mit einer copy() -Funktion, die mit der Signatur übereinstimmt, war in Kotlin 1.2 veraltet und in Kotlin 1.3 verboten.
  • Eine data -Klasse kann nicht von einer anderen data -Klasse erweitert werden.
  • Eine data Klasse kann andere Klassen erweitern (seit Kotlin 1.1)

Datenklassen sind erstklassige Bürger in Kotlin. In kürzester Zeit bieten sie eine reibungslose Lösung mit allen Vorteilen und ohne Kompromisse.

Was bedeutet das für Android?

Ich werde versuchen zu erklären, welche Hauptvorteile ich bei der Verwendung von Kotlin-Datenklassen in meinen Android-Projekten gefunden habe. Dies sind nicht alle und möglicherweise nicht die wichtigsten, aber dies sind die offensichtlichsten aus meiner bisherigen Erfahrung.

  • Datenmodelle

Architektur in Android war (und ist) ein heißes Thema. Es gibt mehrere Optionen, aber die meisten von ihnen haben die Trennung von Bedenken und Prinzipien der Einzelverantwortung gemeinsam. Saubere Architektur – sehr berühmt in der Android-Community – ist eine Reihe von guten Praktiken zu folgen, wenn Sie eine gute Architektur anstreben, die Wert auf die Verwendung verschiedener Modelle für verschiedene Ebenen der Architektur legt. Dies bedeutet, dass ein Projekt mit 3 oder 4 verschiedenen Datenmodellen zum Standard wird.

Das bedeutet, dass die Anzahl der Datenklassen in einem Projekt sehr hoch ist und Vorgänge wie Erstellen, Lesen, Ändern, Kopieren, Vergleichen, Zuordnen … Datenmodellklassen tägliche Aufgaben sind, die von der Kotin-Datenklasse profitieren automatisch generierter Code. Spart viel Zeit und reduziert die Möglichkeit, Fehler einzuführen.

  • Kein automatischer Wert mehr

Die Verwendung von Werttypen ist in Android eine gute Praxis, insbesondere bei Datenklassen.

Ein Werttyp ist ein Objekt aus einer unveränderlichen Wertklasse.
Eine Wertklasse ist eine Klasse, deren Gleichheit von ihrem Inhalt abhängt.
Eine unveränderliche Klasse ist eine Klasse, die nach der Erstellung nicht geändert werden kann.

AutoValue ist eine beliebte Bibliothek von Google, mit der wir Werttypen erstellen können. Es macht seinen Job, aber es ist sehr ausführlich für etwas, das nicht so kompliziert sein sollte.

Die Verwendung der kotlin data -Klassen mit dem val -Zugriffsmodifikator gibt uns eine nahe genug Annäherung an Werttypen.

data class User(val name : String, val age : Int)

Die vorherige Benutzerklasse ist eine Klasse, die in einer viel kürzeren Syntax nahe genug an Werttypen liegt. Für einen guten Teil der Leute kann AutoValue durch Kotlin ersetzt werden. Weniger Code, keine Annotationsverarbeitung und eine Bibliothek weniger, auf die man sich verlassen kann.

Hinweis: Kotlin bietet schreibgeschützte Eigenschaften und Klassen mit dem Schlüsselwort val . Schreibgeschützt und unveränderlich ist nicht dasselbe (mehr hier), wird aber im Allgemeinen für praktische Zwecke als gut genug angesehen.

  • Kein Lombok mehr

Eine der Möglichkeiten, wie Entwickler beim Umgang mit Datenklassen Zeit sparen wollten, ist die Verwendung von Bibliotheken zum Generieren von Getter- und Setter-Methoden. Lombok ist einer der (in) berühmten in Android / Java. Es erfordert nicht nur die Bibliothek, sondern auch ein Plugin für AS. Die lange Geschichte kurz ist für die meisten Entwickler Lombok bringen so viele Vorteile wie Kopfschmerzen, so wird es, dass Bibliothek, die Sie beginnen zu lieben, aber nach einer Weile können Sie nicht warten, um loszuwerden, aber Sie nie tun, weil ist überall.

Da Kotlin-Datenklassen keine Getter / Setter-Methoden manuell schreiben müssen, ist der Hauptbedarf für Lombok weg.

  • RecyclerView DiffUtil

RecyclerView in Android ist das Widget. Es ist in jeder App in mehreren Bildschirmen. Eine wichtige Komponente bei der Implementierung von RecyclerView-Adaptern ist die DiffUtil-Klasse. DiffUtil berechnet den Unterschied zwischen zwei Listen, um die Änderungen – falls vorhanden – an den Adapter zu senden. Es ist viel effizienter, als die gesamte Liste immer wieder zu aktualisieren, und es animiert die Änderungen wunderschön.

DiffUtil beruht stark auf der Gleichheit der Elemente, was bedeutet, dass zwei Elemente gleich sind, wenn ihr Inhalt gleich ist. Jedes Mal, wenn Sie eine RecyclerView verwenden, sollten Sie DiffUtil verwenden, was bedeutet, dass Sie vergleichen müssen, ob zwei Objekte dieselben Informationen enthalten. Dies ist einer der Gründe, warum wir equals in unseren Java-Datenklassen überschreiben müssen, aber mit Kotlin data -Klassen ist es kostenlos.

Hinweis: Wenn Sie mit DiffUtil nicht vertraut sind, lesen Sie diese Kurzanleitung.

  • Tests

In Testklassen prüfen wir ständig, ob die erwarteten Werte mit den tatsächlichen Werten übereinstimmen. Das Vergleichen der Objektgleichheit (wie zuvor beschrieben) ist eine sehr häufige Aufgabe.

Selbst wenn Sie das Triplett equals , toString und toHash für Ihre reguläre App-Laufzeit nicht überschreiben müssen, besteht eine hohe Wahrscheinlichkeit, dass Sie diese Methoden zu Testzwecken überschreiben müssen Kotlin-Datenklassen Löschen Sie den Weg zum Testen ohne Ausreden.

Nachtrag

Lassen Sie uns über einige andere häufige Szenarien sprechen, die bei der Arbeit mit Kotlin-Datenklassen erwähnenswert sind. Dies hängt nicht unbedingt mit Datenklassen zusammen, ist aber unter ihnen besonders häufig.

  • Mehrere Konstruktoren
data class User(val name : String, val age : Int)

In der zuvor definierten Klasse User müssen wir beim Erstellen einer Instanz wie User("Steve", 56) explizit name und age angeben.

In Kotlin können wir Standardwerte für Argumente so definieren, dass, falls wir keinen Wert für dieses Argument übergeben, der Standardwert zugewiesen wird.

data class User(val name : String, val age :Int = 0)

User("Steve", 56) ist immer noch gültig, aber jetzt ist ein zweiter Konstruktor User("Steve") erlaubt. In diesem Fall ist der Wert age 0.

Wir können beiden Parametern Standardwerte zuweisen, wodurch ein dritter Konstruktor User() ermöglicht wird, wobei name leer und age 0 ist.

data class User(val name : String = "", val age : Int = 0)

Also jetzt User() , User("Steve") und User("Steve",56) alle sind gültige Anrufe.

Dies hat einige Einschränkungen: Optionale Parameter müssen die letzten Parameter im Konstruktor sein. Der nächste wird nicht kompiliert.

data class User(val name : String = "", val age : Int)
val user = User(56) // This doesn't compile

Eine weitere Einschränkung besteht darin, dass mehrere optionale Parameter von rechts nach links übersprungen werden müssen.

data class User(
val name : String,
val surname : String = "",
val age : Int = 0
)User("Steve")
User("Steve", "Jobs")
User("Steve", "Jobs", 56)
User("Steve",56) // This wont compile

Um mit diesen Einschränkungen umzugehen, bietet Kotlin benannte Argumente an. Auf diese Weise können wir angeben, zu welchem Argument zu jedem Wert gehört. Jetzt können wir Dinge wieUser(name = "Steve", age = 56) — oder kürzer User("Steve", age = 56) — tun, wobei der Nachname dem Standardwert zugewiesen wird.

Standardargumente und benannte Argumente sind eine sehr praktische Möglichkeit, mehrere Konstruktoren und Überladungen aus einer sehr kompakten Deklaration anzubieten.

  • @ JvmOverloads

Alle im vorherigen Punkt erläuterten Aufrufe berücksichtigen keine Aufrufe von der Java-Seite. Kotlin ist mit Java interoperabel, daher müssen wir in der Lage sein, Instanzen der Klasse User aus Java zu erstellen.

Wenn Sie es nicht von Java aus verwenden, sind Sie fertig, aber ansonsten benötigen Sie die Annotation @JvmOverloads, da Java keine benannten Argumente bietet.

data class User @JvmOverloads constructor(
val name : String,
val surname : String = "",
val age : Int = 0
)

Was dies tut Generieren mehrerer Konstruktoren, damit sie von Java aus aufgerufen werden können.

Hinweis: Es ist wichtig zu beachten, dass nicht jede mögliche Permutation erstellt wird, sondern die, die sich aus dem Entfernen der optionalen Argumente von rechts nach links ergeben. Mehr hier

  • Builders

Da Kotlin benannte Parameter und Standardargumente bietet, ist es weniger wahrscheinlich, dass Builders benötigt werden. Darüber hinaus zeigen moderne IDEs wie Android Studio bereits den Namen des Parameters auf der aufrufenden Seite an, was das Lesen erleichtert und ihn nur aus Gründen der Lesbarkeit weniger interessant macht.

In den Fällen, in denen das Builder-Muster noch benötigt wird. Kotlin bietet nichts Besonderes, um uns dabei zu helfen. Ein Builder-Muster in Kotlin sieht so aus:

  • Anmerkungen

Es ist sehr üblich, dass einige Datenmodellklassen die Anmerkungsverarbeitung verwenden. Beispielsweise werden API-Modelle (Datenklassen zur Darstellung deserialisierter Antworten aus der API) häufig mit Gson- oder Moshi-Anmerkungen versehen. Die Persistenzmodelle (Datenklassen zur Darstellung von Daten, die in lokalen Speichern / Datenbanken gespeichert sind) werden häufig mit Room- oder Realm-Annotationen versehen.

In Kotlin können wir diese Anmerkungen weiterhin verwenden. Dies ist beispielsweise ein Benutzermodell, das in der Raumdatenbank gespeichert werden soll:

Zusammenfassung

Kotlin-Datenklassen sind das Ergebnis jahrelangen Lernens aus Schmerz und Frustration mit Datenklassen in Java. Sie zielen darauf ab, alle Vorteile und keine der Nachteile zu haben. Sie zu benutzen ist sehr einfach und angenehm und sobald man sich daran gewöhnt hat, ist es sehr schwierig, zurückzublicken.

In Android helfen sie uns auf verschiedene Arten, aber meistens sparen sie viel Zeit und reduzieren Fehler.

Eine Kotlin-Datenklasse ist ein gutes Beispiel dafür, was Kotlin als Programmiersprache ist: prägnant, pragmatisch und eine Freude für Entwickler.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

lg