我们会经常创建一些只用来保存数据的类。在这种类中一些标准功能和效用的函数通常可以根据数据机械地推导出来。在Kotlin中,它被称作数据类,并被标记为data
:
data class User(val name: String, val age: Int)
编译器会根据主构造函数中所声明的所有属性自动推导出下列成员:
equls()
/ hashcode()
对"User(name=John, age=42)"
格式的 toString()
方法componentN(.)
函数按声明顺序对应于所有属性copy()
函数其中componentN(.)函数会在后面的章节详细讲解,这里暂时不管
为了确保生成的代码的一致性和有意义的行为,数据类必须满足以下要求:
val
或var
标记abstract
,open
,sealed
或 inner
此外,成员的生成遵循成员继承的一些规则:
open
的 componentN()
函数并且返回兼容的类型, 那么会为数据类生成相应的函数,并覆盖超类的实现。如果超类型的这些函数由于签名不兼容或者是 final
而导致无法覆盖,那么会报错;componentN()
和 copy()
方法不支持显示实现从Kotlin 1.1起,数据类可以继承其它类
在JVM平台上,如果生成的类需要无参构造函数,所有属性都要指定默认值。
data class User(val name: String = "", val age: Int = 0)
注意编译器只会为主构造函数中的属性自动生成函数,在类体中声明的属性将被排除在外。
data class Person(val name: String) {
var age: Int = 0
}
上面这个Person
类,只有属性name
会被使用在toString()
,equals()
,hashCode()
和 copy()
的实现中,并且只会有一个component
函数component1()
。当两个Person
对象的age
值不同时,它们仍然是equal
的,因为默认的equals()
方法不会考虑age
属性,只考虑主构造函数中的属性。
@Test
fun testEquals(): Unit {
val person1 = Person("crx")
val person2 = Person("crx")
person1.age = 2
person2.age = 6
assertTrue(person1.equals(person2))
}
上面的测试代码不会报错
通常我们需要复制一个对象,只改变其某些属性,其它属性保持不变。copy()
方法就是用来做这个的。对于上面的User类,其实现方法如下:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
它允许我们这样写:
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
数据类生成的component
函数允许它们使用解构声明。
val jane = User("Jane", 35)
val(name, age) = jane
print("$name, $age years of age")
会打印出:Jane, 35 years of age
解构声明将在后面的章节细讲
Kotlin标准库中提供了Pair
和Triple
方法。在大多数情况下,使用命名的数据类是更好的设计选择,因为提供有意义的名字和属性会让代码更具可读性。
Pair
使用方法如下:
val(name, age) = Pair("Jane", 23)
print("$name, $age years of age")
打印结果和上面的代码一样。
Triple
使用方法和Pair
相似,接收三个参数。