Kotlin Data Classes and Sealed Classes
Data Classes
Kotlin can create a class that only contains data, with the keyword:data:
data class User(val name: String, val age: Int)
The compiler automatically extracts the following functions from the primary constructor based on all declared properties:
-
equals()/hashCode() -
toString()The format is like:"User(name=John, age=42)" -
componentN() functionsCorresponds to the properties, arranged in declaration order. -
copy()Functions
If these functions are already explicitly defined in the class, or inherited from a superclass, they will not be generated.
To ensure consistency and meaningfulness of the generated code, data classes must satisfy the following conditions:
The primary constructor must contain at least one parameter.
All parameters of the primary constructor must be marked as
valorvar;Data classes cannot be declared as
abstract,open,sealedorinner;Data classes cannot inherit other classes (but can implement interfaces).
Copy
Copying uses the copy() function. We can use this function to copy an object and modify some properties. For the User class above, its implementation would look something like this:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
Example
Use the copy class to copy the User data class and modify the age property:
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)
}
The output result is:
User(name=Jack, age=1) User(name=Jack, age=2)
Data Classes and Destructuring Declarations
Component functions allow data classes to be used in destructuring declarations:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
Standard Data Classes
The standard library providesPairandTripleIn most cases, named data classes are a better design choice because they make the code more readable and provide meaningful names and properties.
Sealed Classes
Sealed classes are used to represent restricted class inheritance structures: when a value is of a limited set of types, and cannot be of any other type. In a sense, they are extensions of enum classes: the set of values of an enum type is also restricted, but each enum constant exists only as a single instance, whereas a subclass of a sealed class can have multiple instances that can contain state.
To declare a sealed class, usesealedto modify the class. Sealed classes can have subclasses, but all subclasses must be nested inside the sealed class.
sealed cannot modify interface or abstract class (it will report a warning, but there will not be a compilation error).
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
}
The key benefit of using sealed classes is that when using a when expression, if the statement can be verified to cover all cases, there is no need to add an else clause to the statement.
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` 子句,因为我们已经覆盖了所有的情况
}
Other Extensions