声明数据类
在 Java 中 我们有 Bean
类的概念,他们只是用来保存数据的类,在 Kotlin 中他们称为 数据类 ,使用关键字 data
标记。
data class User(val name: String, val age: Int)
编译器自动从 主构造函数中声明的所有属性 生成以下函数:
- equals() / hashCode() 对
- toString() 格式是 “User(name=John, age=42)”
- componentN() 函数 按声明顺序对应于所有属性
- copy() 函数。
生成的具体 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.checkParameterIsNotNull(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.checkParameterIsNotNull(name, "name");
return new User(name, age);
}
// $FF: synthetic method
// $FF: bridge method
@NotNull
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);
}
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}
public int hashCode() {
return (this.name != null ? this.name.hashCode() : 0) * 31 + this.age;
}
public boolean equals(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;
}
}
}
为了确保数据类生成的 Java 代码的一致性并且有所意义,数据类需要满足以下要求:
- 主构造函数需要至少有一个参数;
- 主构造函数的
所有参数
需要标记为val
或var
; - 数据类不能是抽象、开放、密封或者内部的;
- (在1.1之前)数据类
只能实现接口
。
注意:在 JVM 中,如果生成的类需要含有一个无参的构造函数,则所有的属性必须指定默认值。
data class User(val name: String = "", val age: Int = 0)
在类体中声明的属性
在上面 编译器根据数据类的主构造器中的所有属性生成函数 可知,如果不需要为一个属性生成相应函数只需要把具体属性声明在类体中:
data class User(val name: String) {
var age: Int = 0
}
那么在具体生成的代码中 toString()
、 equals()
、 hashCode()
以及 copy()
的实现中只会用到 name
属性,并且只有一个 component
函数 —- component1()
。如果两个 User
对象有不同的年龄,但是它们会被视为相等 。
data class User (val name: String) {
var age: Int = 0
}
fun main(args: Array<String>) {
//sampleStart
val user1= User ("John")
val user2 = User ("John")
user1.age = 10
user2.age = 20
//sampleEnd
println("user1 == user2: ${user1 == user2}")
println("user1 with age ${user1.age}: ${user1}")
println("user2 with age ${user2.age}: ${user2}")
}
打印日志为:
user1 == user2: true
user1 with age 10: User(name=John)
user2 with age 20: User(name=John)
复制
在一些情况下我们需要复制一个对象,并改变它的一些属性但其他部分保持不变,数据类的 copy()
函数就是为此需求而生,对于 User
类其实现类似于下面:
fun copy(name: String = this.name, age: Int = this.age) = User(n
ame, age)
这让我们可以这样写:
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
数据类于解构声明
为数据类生成的 Component
函数 使它们可在解构声明中使用:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // 输出 "Jane, 35 years of ag
e"
标准数据类
标准库提供了 Pair
与 Triple
。尽管在很多情况下命名数据类是更好的设计选
择, 因为它们通过为属性提供有意义的名称使代码更具可读性。