阅读完需:约 6 分钟
数据类
数据类是一个简单的类,用于保存数据/状态并包含标准功能(函数)。 data
关键字用于将类声明为数据类。
data class User(val name: String, val age: Int)
声明数据类必须至少包含一个带有属性参数(val
或var
)的主构造函数。
数据类内部有以下函数:
equals(): Boolean
hashCode(): Int
toString(): String
-
component()
函数对应的属性 copy()
要创建数据类,首先需要满足以下要求:
- 包含具有至少一个参数的主构造函数。
- 主构造函数的参数标记为
val
或var
。 - 数据类不能是抽象的,内部的,开放的或密封的。
- 在
1.1
版本之前,数据类只实现接口。1.1
版本之后,数据类可以扩展其他类。
由于数据类内部存在上述函数,因此数据类消除了反复套用代码。

java 中的javaBean 和 kotlin 的data class 看似很类似,但是这两个不相等的。
在 data class 中有很多方法:
component(解构)
可以使用解构声明将一个对象分解为多个变量



例子:
data class User(val name: String, val age: Int, val gender: String)
fun main(args: Array<String>) {
val u1 = User("John", 29, "Male")
val (name, age, gender) = u1
println("name = $name")
println("age = $age")
println("gender = $gender")
}
运行该程序时,输出为:
name = John
age = 29
gender = Male
这是可能的,因为编译器会为数据类生成component()函数的所有属性。 例如:
data class User(val name: String, val age: Int, val gender: String)
fun main(args: Array<String>) {
val u1 = User("John", 29, "Male")
println(u1.component1()) // John
println(u1.component2()) // 29
println(u1.component3()) // "Male"
}
运行该程序时,输出为:
John
29
Male
copy() 函数 – 复制
对于数据类,可以使用copy()函数创建具有不同属性的对象副本。 它的工作原理如下:
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
val u1 = User("John", 29)
//使用复制函数创建对象
val u2 = u1.copy(name = "Randy")
println("u1: name = ${u1.name}, name = ${u1.age}")
println("u2: name = ${u2.name}, name = ${u2.age}")
}
运行该程序时,输出为:
u1: name = John, name = 29
u2: name = Randy, name = 29
toString()方法 – 返回字符串
oString()函数返回该对象的字符串表示形式。
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
val u1 = User("John", 29)
println(u1.toString())
}
运行该程序时,输出为:
User(name=John, age=29)
hashCode() 和 equals()
hasCode() 方法返回对象的散列码。 如果两个对象相等,则hashCode()会产生相同的整数结果。
如果两个对象相等(hashCode()相同),则equals()返回true。如果对象不相等,equals()返回false。
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
val u1 = User("John", 29)
val u2 = u1.copy()
val u3 = u1.copy(name = "Amanda")
println("u1 hashcode = ${u1.hashCode()}")
println("u2 hashcode = ${u2.hashCode()}")
println("u3 hashcode = ${u3.hashCode()}")
if (u1.equals(u2) == true)
println("u1 等于 u2.")
else
println("u1 不等于 u2.")
if (u1.equals(u3) == true)
println("u1 等于 u3.")
else
println("u1 不等于 u3.")
}
运行该程序时,输出为:
u1 hashcode = 71750738
u2 hashcode = 71750738
u3 hashcode = 771732263
u1 等于 u2.
u1 不等于 u3.
对比JavaBean

data class不能被继承
可以先看一下kotlin为data类生成的对应的java类是什么样的,查看方式,doule shift键,然后Actions中输入kotlin Bytecode显示:

点击Decompile即可查看对应生成的java代码

以下是Book的数据类对应生成的java代码
val book = Book(0, "Kotlin in Action", Person(1, "Dmitry", 40))
public final class Book {
private final long id;
@NotNull
private final String name;
@NotNull
private final Person author;
public final long getId() {
return this.id;
}
@NotNull
public final String getName() {
return this.name;
}
@NotNull
public final Person getAuthor() {
return this.author;
}
public Book(long id, @NotNull String name, @NotNull Person author) {
Intrinsics.checkNotNullParameter(name, "name");
Intrinsics.checkNotNullParameter(author, "author");
super();
this.id = id;
this.name = name;
this.author = author;
}
public final long component1() {
return this.id;
}
@NotNull
public final String component2() {
return this.name;
}
@NotNull
public final Person component3() {
return this.author;
}
@NotNull
public final Book copy(long id, @NotNull String name, @NotNull Person author) {
Intrinsics.checkNotNullParameter(name, "name");
Intrinsics.checkNotNullParameter(author, "author");
return new Book(id, name, author);
}
// $FF: synthetic method
public static Book copy$default(Book var0, long var1, String var3, Person var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.id;
}
if ((var5 & 2) != 0) {
var3 = var0.name;
}
if ((var5 & 4) != 0) {
var4 = var0.author;
}
return var0.copy(var1, var3, var4);
}
@NotNull
public String toString() {
return "Book(id=" + this.id + ", name=" + this.name + ", author=" + this.author + ")";
}
public int hashCode() {
long var10000 = this.id;
int var1 = (int)(var10000 ^ var10000 >>> 32) * 31;
String var10001 = this.name;
var1 = (var1 + (var10001 != null ? var10001.hashCode() : 0)) * 31;
Person var2 = this.author;
return var1 + (var2 != null ? var2.hashCode() : 0);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Book) {
Book var2 = (Book)var1;
if (this.id == var2.id && Intrinsics.areEqual(this.name, var2.name) && Intrinsics.areEqual(this.author, var2.author)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
除了类名方法名前面添加了final
关键字以外,生成了许多方法,其中有重写hashCode()
和equals()
方法,所以如果有一个类继承了data类,可能导致属性变化,从而导致hashCode()
和equals()
方法的结果不一致。

合理使用 data class
data类的属性最好全部为基本类型或者其他data类型,保持它的纯净性。




另外,有方法可以破除data
类的不可继承性,其实如果你想用一个可以继承到类,只需要把data
关键字去掉,建一个普通类就好了。kotlin这样设计肯定是想保持它的纯洁性,如果可继承,只会变的更复杂。
破除data
类的不可继承性需要额外添加两个插件:

plugins {
...
id 'org.jetbrains.kotlin.plugin.noarg' version '1.4.20'
id 'org.jetbrains.kotlin.plugin.allopen' version '1.4.20'
}
noArg {
invokeInitializers = true
annotations "com.bennyhuo.kotlin.advancedtypes.dataclasses.PoKo"
}
allOpen {
annotations "com.bennyhuo.kotlin.advancedtypes.dataclasses.PoKo"
}
在data类上加注解即可
@PoKo
data class Book(val id: Long, val name: String, val author: Person)
这时同样去查看Book的数据类对应生成的java代码会发现,之前的类名和get方法名前面的final
关键字被移除了,也就是可以被继承使用了,同时会生成一个无参的构造函数。