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
关键字声明 - 至少有一个主构造函数参数
- 所有主构造函数参数必须是
val
或var
- 数据类不能是
open
、sealed
、abstract
或inner
类 - 不能继承自其他类(除非使用
@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
) - 主构造函数参数必须为
val
或var
如需进一步了解 Kotlin 的特性,欢迎阅读我们的 Kotlin 与 Java 互操作性 和 Kotlin 入门教程。
完整代码示例请查看 GitHub:Kotlin 教程仓库。