ZetCode

Kotlin data 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的 data 关键字简化了创建主要用于存储数据的类。 它会自动生成有用的方法,例如 toStringequalshashCode。 本教程通过实际示例深入探讨了 data 关键字。

基本定义

Kotlin 中的 data 关键字将一个类标记为数据类。 数据类旨在存储数据,并自动为标准方法提供实现。 它们非常适合创建具有最少样板代码的 POJO(普通旧 Java 对象)。

基本数据类

一个简单的数据类需要在类声明之前使用 data 关键字。 主构造函数必须至少有一个参数。 所有参数都将自动成为属性。

BasicDataClass.kt
package com.zetcode

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

fun main() {

    val person = Person("John Doe", 30)
    println(person) // Output: Person(name=John Doe, age=30)
}

这里,我们定义了一个具有两个属性的 Person 数据类。 Kotlin 自动生成一个可读的 toString 方法。 输出以标准化格式显示属性名称及其值。

Equals 和 HashCode

数据类根据在主构造函数中声明的所有属性自动实现 equalshashCode 方法。 这确保了正确的相等比较。

EqualsHashCode.kt
package com.zetcode

data class Product(val id: Int, val name: String, val price: Double)

fun main() {

    val p1 = Product(1, "Laptop", 999.99)
    val p2 = Product(1, "Laptop", 999.99)
    
    println(p1 == p2) // Output: true
    println(p1.hashCode() == p2.hashCode()) // Output: true
}

具有相同属性值的两个 Product 实例被视为相等。 hashCode 值也匹配,这使得它们适合在基于哈希的集合中使用,例如 HashSetHashMap

Copy 函数

数据类提供一个 copy 函数,该函数创建一个新实例,其中一些属性已更改。 这对于希望创建修改后的副本的不可变数据结构很有用。

CopyFunction.kt
package com.zetcode

data class Book(val title: String, val author: String, val year: Int)

fun main() {

    val book1 = Book("Kotlin in Action", "Dmitry Jemerov", 2017)
    val book2 = book1.copy(title = "Kotlin Cookbook", year = 2022)
    
    println(book1) // Output: Book(title=Kotlin in Action, ...)
    println(book2) // Output: Book(title=Kotlin Cookbook, ...)
}

copy 函数创建一个新的 Book 实例,该实例具有相同的作者,但标题和年份不同。 在新实例中保留了在 copy 中未指定的原始属性。

解构声明

数据类启用解构声明,允许您将一个对象拆包成多个变量。 这有效,因为数据类会自动提供 componentN 函数。

Destructuring.kt
package com.zetcode

data class Point(val x: Int, val y: Int)

fun main() {

    val point = Point(10, 20)
    val (xCoord, yCoord) = point
    
    println("X: $xCoord, Y: $yCoord") // Output: X: 10, Y: 20
}

Point 实例被解构为 xCoordyCoord 变量。 变量的顺序与主构造函数声明中的属性顺序匹配。

默认和命名参数

数据类在其构造函数中支持默认值和命名参数。 这在创建实例的同时提供了灵活性,同时保持了代码的整洁。

DefaultArgs.kt
package com.zetcode

data class User(
    val username: String,
    val email: String = "",
    val active: Boolean = true
)

fun main() {

    val user1 = User("johndoe")
    val user2 = User("janedoe", "jane@example.com", false)
    val user3 = User(username = "admin", active = true)
    
    println(user1) // Output: User(username=johndoe, email=, active=true)
    println(user2) // Output: User(username=janedoe, email=jane@example.com, active=false)
    println(user3) // Output: User(username=admin, email=, active=true)
}

User 数据类演示了 emailactive 属性的默认值。 我们可以使用不同的参数组合创建实例,使用位置参数或命名参数。

数据类的限制

数据类有一些限制。 它们不能是抽象的、开放的、密封的或内部的。 它们必须具有一个主构造函数,该构造函数至少具有一个参数,并且所有参数都必须标记为 valvar

Limitations.kt
package com.zetcode

// This won't compile - data class can't be abstract
// abstract data class Shape(val sides: Int)

// This won't compile - data class needs at least one parameter
// data class Empty()

fun main() {

    data class Temp(val value: Int) // Local data classes are allowed
    
    val temp = Temp(42)
    println(temp) // Output: Temp(value=42)
}

注释的示例显示了无效的数据类声明。 但是,数据类可以在函数中本地声明,如 Temp 类所示。 这对于临时数据结构很有用。

带有附加成员的数据类

虽然数据类主要处理数据,但它们可以包含其他成员,例如方法或辅助构造函数。 但是,仅主构造函数属性用于生成的 方法。

AdditionalMembers.kt
package com.zetcode

data class Employee(
    val id: Int,
    val name: String
) {
    var department: String = "Unassigned"
    
    fun promote() {
        department = "Management"
    }
}

fun main() {

    val emp = Employee(101, "Alice")
    emp.department = "Engineering"
    emp.promote()
    
    println(emp) // Output: Employee(id=101, name=Alice)
    println("Department: ${emp.department}") // Output: Department: Management
}

Employee 数据类包括一个附加属性和方法。 请注意, toString 输出仅包括主构造函数属性。 必须单独访问其他成员。

数据类的最佳实践

来源

Kotlin 数据类文档

本教程深入介绍了 Kotlin 的 data 关键字,展示了如何有效创建和使用数据类。 我们探讨了自动方法生成、复制、解构和限制。 数据类减少了样板代码,同时为以数据为中心的类提供了强大的功能。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有多年的编程经验。 自 2007 年以来,我一直在撰写编程文章。 到目前为止,我写了 1400 多篇文章和 8 本电子书。 我有超过八年的编程教学经验。

列出 所有 Kotlin 教程