Kotlin-Datenklassen und versiegelte Klassen
Datenklassen
Kotlin kann eine Klasse erstellen, die nur Daten enthält, mit dem Schlüsselwort:data:
data class User(val name: String, val age: Int)
Der Compiler extrahiert automatisch die folgenden Funktionen aus dem Primärkonstruktor basierend auf allen deklarierten Eigenschaften:
-
equals()/hashCode() -
toString()Das Format sieht wie folgt aus:"User(name=John, age=42)" -
componentN() functionsEntspricht den Eigenschaften, angeordnet in Deklarationsreihenfolge. -
copy()Funktionen
Wenn diese Funktionen bereits explizit in der Klasse definiert oder von einer Oberklasse geerbt wurden, werden sie nicht generiert.
Um Konsistenz und Aussagekraft des generierten Codes zu gewährleisten, müssen Datenklassen die folgenden Bedingungen erfüllen:
Der Primärkonstruktor muss mindestens einen Parameter enthalten.
Alle Parameter des Primärkonstruktors müssen markiert werden als
valodervar;Datenklassen können nicht deklariert werden als
abstract,open,sealedoderinner;Datenklassen können nicht von anderen Klassen erben (können aber Schnittstellen implementieren).
Copy
Das Kopieren verwendet die copy()-Funktion. Wir können diese Funktion verwenden, um ein Objekt zu kopieren und einige Eigenschaften zu ändern. Für die obige User-Klasse würde die Implementierung ungefähr so aussehen:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
Beispiel
Verwenden Sie die copy-Klasse, um die User-Datenklasse zu kopieren und die Eigenschaft age zu ändern:
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
println(jack)
println(olderJack)
}
Das Ausgabeergebnis ist:
User(name=Jack, age=1) User(name=Jack, age=2)
Datenklassen und destrukturierende Deklarationen
Komponentenfunktionen ermöglichen es, Datenklassen in destrukturierenden Deklarationen zu verwenden:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
Standard-Datenklassen
Die Standardbibliothek bietetPairundTripleIn den meisten Fällen sind benannte Datenklassen eine bessere Designentscheidung, weil sie den Code lesbarer machen und aussagekräftige Namen und Eigenschaften bieten.
Sealed-Klassen
Sealed-Klassen werden verwendet, um eingeschränkte Klassenvererbungsstrukturen darzustellen: wenn ein Wert zu einer begrenzten Menge von Typen gehört und zu keinem anderen Typ gehören kann. In gewissem Sinne sind sie Erweiterungen von Enum-Klassen: Die Menge der Werte eines Enum-Typs ist ebenfalls eingeschränkt, aber jede Enum-Konstante existiert nur als einzelne Instanz, während eine Unterklasse einer Sealed-Klasse mehrere Instanzen haben kann, die Zustand enthalten können.
Um eine Sealed-Klasse zu deklarieren, verwenden Siesealed, um die Klasse zu modifizieren. Sealed-Klassen können Unterklassen haben, aber alle Unterklassen müssen innerhalb der Sealed-Klasse verschachtelt sein.
sealed kann keine Schnittstelle oder abstrakte Klasse ändern (es wird eine Warnung ausgegeben, aber es wird kein Kompilierungsfehler auftreten).
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
Der Hauptvorteil der Verwendung von sealed-Klassen besteht darin, dass bei Verwendung eines when-Ausdrucks, wenn überprüft werden kann, dass die Anweisung alle Fälle abdeckt, keine else-Klausel zur Anweisung hinzugefügt werden muss.
fun eval(expr: Expr): Double = when(expr) {
is Expr.Const -> expr.number
is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
Expr.NotANumber -> Double.NaN
// 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}
Andere Erweiterungen