Kotlin 中的 data
关键字用于声明数据类(Data Class),它专门为简化数据模型的实现而设计的。
1 主构造函数必须至少有一个参数,且参数标记为 val
或 var
数据类的主构造函数必须至少有一个参数,并且这些参数必须使用 val
或 var
声明。普通类则没有这样的要求。
正确的数据类定义:
data class User(val name: String, val age: Int)
普通类的主构造函数可以没有参数要求:
class Student
class Person(val name: String, val age: Int)
2 自动生成标准方法
数据类会自动生成以下方法,而普通类需要手动实现或依赖 IDE 生成。
以下是定义的一个数据类 User
:
data class User(val name: String, val age: Int)
反编译成 Java 代码:
public final class User {
@NotNull
private final String name;
private final int age;
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public User(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
@NotNull
public final String component1() {
return this.name;
}
public final int component2() {
return this.age;
}
@NotNull
public final User copy(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
return new User(name, age);
}
// $FF: synthetic method
public static User copy$default(User var0, String var1, int var2, int var3, Object var4) {
if ((var3 & 1) != 0) {
var1 = var0.name;
}
if ((var3 & 2) != 0) {
var2 = var0.age;
}
return var0.copy(var1, var2);
}
@NotNull
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}
public int hashCode() {
String var10000 = this.name;
return (var10000 != null ? var10000.hashCode() : 0) * 31 + Integer.hashCode(this.age);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof User) {
User var2 = (User)var1;
if (Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
return true;
}
}
return false;
} else {
return true;
}
}
}
通过上述代码可以看出,数据类自动生成了以下方法:
toString()
:生成格式化的字符串表示,如:User(name=John, age=30)
;componentN()
函数:支持解构声明访问属性;equals()
和hashCode()
:比较两个对象的内容是否相等(而非引用相等),仅基于主构造函数中声明的属性;copy()
方法:快速创建对象的副本,并可选择性修改部分属性(适用于不可变对象);
以下是使用示例:
data class User(val name: String, val age: Int)
class Person(val name: String, val age: Int)
fun main() {
val user = User("John", 30)
// toString 格式化输出
println(user.toString()) // User(name=John, age=30)
// 解构声明,解构为 name 和 age
val (name, age) = user
println("name = $name, age = $age") // name = John, age = 30
// equals() 比较内容
val user1 = User("Danny", 31)
val user2 = User("Danny", 31)
// 数据类自动重写了 equals() 方法,比较内容
println("user1 == user2 ? ${user1 == user2}") // user1 == user2 ? true,
val person1 = Person("Danny", 31)
val person2 = Person("Danny", 31)
// 普通类默认比较引用
println("person1 == person2 ? ${person1 == person2}") // person1 == person2 ? false
// copy() 创建副本
val newUser = user.copy(age = 32)
println("newUser.age = ${newUser.age}") // newUser.age = 32
}
注意:
- 仅主构造函数的属性参与方法的生成: 类体中定义的属性不会影响
equals
、hashCode
等方法; - 不可变推荐:数据类通常设计为不可变(属性用
val
声明),避免因修改状态导致意外问题; - 深拷贝与浅拷贝:
copy
方法是浅拷贝,若属性是引用类型(如集合、对象)、需自行处理深拷贝逻辑;
3 数据类不能是 abstract
、open
、sealed
或 inner
类
从 Kotlin 1.1 开始,数据类可以继承其他类。