1. 概述

Kotlin 引入了 数据类(Data Class) 的概念,其核心目的是简化 仅用于存储数据的类(POJO) 的创建过程。相比 Java 中冗长的样板代码,如 getter、setter、equals()hashCode() 等方法,Kotlin 通过数据类自动为我们生成这些代码,使模型类更简洁、可读性更强。

本文将介绍 Kotlin 数据类的使用方式,并与 Java 中的传统写法进行对比,帮助你快速理解其优势和使用限制。

2. Kotlin 项目搭建

关于如何搭建 Kotlin 项目,请参考我们的 Kotlin 入门教程

3. Java 中的 POJO 类

假设我们要在 Java 中定义一个 Task 类,通常需要编写大量样板代码:

public class Task {
    private int id;
    private String description;
    private int priority;

    public Task(int id, String description, int priority) {
        this.id = id;
        this.description = description;
        this.priority = priority;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public float getPriority() {
        return priority;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = Integer.hashCode(this.id) * prime;
        result = prime * result + Integer.hashCode(this.priority);
        result = prime * result + ((this.description == null) ? 0 : this.description.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object var1) {
        if (this != var1) {
            if (var1 instanceof Task) {
                Task var2 = (Task) var1;
                if (this.id == var2.id
                  && Intrinsics.areEqual(this.description, var2.description)
                  && this.priority == var2.priority) {
                    return true;
                }
            }
            return false;
        } else {
            return true;
        }
    }

    @Override
    public String toString() {
        return "Task [id=" + id + ", description=" + description + ", priority=" + priority + "]";
    }
}

上面这个类一共写了 69 行代码,但只包含三个字段。这种写法在 Java 中非常常见,但确实很啰嗦。

4. Kotlin 数据类

现在我们用 Kotlin 的数据类重写这个 Task 类,实现完全相同的功能:

data class Task(
    var id: Int,
    var description: String,
    var priority: Int
)

简洁明了,只需一行声明,Kotlin 会自动为我们生成以下方法:

  • equals()
  • hashCode()
  • toString()
  • copy()
  • component1(), component2(), component3() 等(用于解构)

4.1 使用方式

实例化方式与普通类相同:

val task = Task(1001, "Replace Fuel Tank Filler Caps", 5)

访问属性和方法也很直观:

println(task.id) // 1001
println(task.description) // Replace Fuel Tank Filler Caps
println(task.priority) // 5

task.priority = 4

println(task.toString())

4.2 copy 方法

Kotlin 提供了 copy() 方法用于复制对象并修改部分字段:

val copyTask = task.copy(priority = 4)
println(copyTask.toString())

⚠️ Java 中没有原生的复制机制,通常需要手动实现 clone 方法、使用序列化工具或构造器复制。

4.3 解构声明

Kotlin 支持将对象的属性解构为多个变量:

val (id, description, priority) = task

也可以从函数返回值中直接解构:

fun getTask() = task
val (idf, descriptionf, priorityf) = getTask()

4.4 数据类的限制

使用数据类时需注意以下限制:

  • 类必须使用 data 关键字声明
  • 至少有一个主构造函数参数
  • 所有主构造函数参数必须是 valvar
  • 数据类不能是 opensealedabstractinner
  • 不能继承自其他类(除非使用 @JvmRecord,见下文)

如果需要无参构造函数,所有属性必须提供默认值:

data class Task(var id: Int = 1000, var description: String = "", var priority: Int = 0)

5. Java Records 兼容性

从 Kotlin 1.5 开始,Kotlin 数据类可以编译为 Java 14+ 的 Records。只需在数据类上添加 @JvmRecord 注解即可:

@JvmRecord
data class Person(val firstName: String, val lastName: String)

编译时需启用预览功能(适用于 Java 15 及以下):

>> kotlinc -jvm-target 15 -Xjvm-enable-preview Person.kt

从 Java 16 开始,Records 已稳定,无需启用预览:

>> kotlinc -jvm-target 16 Person.kt

查看生成的字节码会发现该类继承自 java.lang.Record

>> javap -c -p com.baeldung.dataclass.Person
Compiled from "Person.kt"
public final class com.baeldung.dataclass.Person extends java.lang.Record {
    // omitted
}

⚠️ 注意

  • 使用 @JvmRecord 注解时,所有属性必须是 val,不能是 var,因为 Java Records 是不可变的
  • 此类不能继承其他类,因为它已经继承了 Record

6. 小结

Kotlin 数据类极大地简化了 POJO 的编写过程,减少了样板代码,提升了代码可读性和可维护性。它还提供了 copy() 和解构等实用功能,非常适合用于模型类和数据传输对象(DTO)。

✅ 优势总结:

  • 自动生成 equals(), hashCode(), toString()
  • 支持 copy() 实现浅拷贝
  • 支持解构声明
  • 可编译为 Java Records(适用于 Java 14+)

❌ 限制:

  • 不支持继承其他类(除非使用 @JvmRecord
  • 主构造函数参数必须为 valvar

如需进一步了解 Kotlin 的特性,欢迎阅读我们的 Kotlin 与 Java 互操作性Kotlin 入门教程

完整代码示例请查看 GitHub:Kotlin 教程仓库


原始标题:Data Classes in Kotlin