Kotlin数据类基础:从定义到实战
数据类,说白了就是专门用来装数据的类。我刚开始用Kotlin时,最爽的就是这个特性。你想想看,Java里写个POJO类,getter、setter、toString、equals、hashCode,一套组合拳下来,代码量直接翻倍。Kotlin一句data class全搞定。
我个人习惯,只要一个类的主要职责是持有数据,就毫不犹豫用data class。别犹豫,别想着「万一以后要加逻辑怎么办」——真到那时候再改也不迟。
data class的定义
语法很简单,在class前面加个data关键字就行。但有个硬性要求:主构造函数至少有一个参数。为什么?因为数据类的核心就是围绕这些参数展开的。
data class User(
val id: Long,
val name: String,
val email: String
)
就这么一行,抵得上Java里几十行代码。我在项目中接手过一个老系统,光User类就写了200多行,全是模板代码。后来重构时改成data class,整个文件清爽得不像话。
val或var声明。我个人强烈建议用val,保持不可变性。
自动生成的函数
data class会自动生成几个关键函数。咱们一个一个看。
toString()
默认打印所有主构造函数参数的键值对。调试时特别有用。
val user = User(1, "张三", "zhangsan@example.com")
println(user)
// 输出: User(id=1, name=张三, email=zhangsan@example.com)
我曾经在排查线上问题时,全靠这个输出定位数据异常。要是Java里没重写toString,打印出来就是User@1a2b3c4d,啥也看不出来。
equals() 和 hashCode()
这两个是一对,必须一起重写。data class会基于主构造函数的所有参数生成。
val user1 = User(1, "张三", "zhangsan@example.com")
val user2 = User(1, "张三", "zhangsan@example.com")
println(user1 == user2) // true,结构相等
println(user1 === user2) // false,引用相等
==默认调用equals(),===才是比较引用。这和Java不一样,刚转过来的同学容易搞混。
这里有个坑:如果类体里定义了其他属性,它们不会参与equals和hashCode的计算。我在项目中遇到过,有人在data class里加了个var temp用于缓存计算结果,结果两个对象明明数据一样,就因为temp不同被判为不相等。排查了半天才发现问题。
copy()
这是data class的杀手锏。基于当前对象创建一个新对象,同时可以修改部分属性。
val original = User(1, "张三", "zhangsan@example.com")
val modified = original.copy(name = "李四")
println(modified) // User(id=1, name=李四, email=zhangsan@example.com)
你想想看,如果没有copy,要修改一个不可变对象的某个字段,你得手动new一个,把所有字段都传一遍。有了copy,只传要改的就行。这在函数式编程和状态管理中特别有用。
我习惯在UI层大量使用copy。比如用户修改个人信息,每次修改都通过copy生成新对象,旧状态不变。这样撤销操作、历史记录都很好实现。
数据类的解构声明
解构声明,说白了就是把一个对象拆成多个变量。data class默认支持,按主构造函数参数的顺序来。
val (id, name, email) = user
println("ID: $id, 姓名: $name, 邮箱: $email")
这个特性在遍历集合时特别爽:
val users = listOf(
User(1, "张三", "a@b.com"),
User(2, "李四", "c@d.com")
)
for ((id, name) in users) {
println("$id: $name")
}
不用写user.id、user.name,直接拆开用。代码简洁不少。
_占位:val (_, name) = user。
我在写API响应解析时经常这么用。后端返回的JSON解析成data class,然后直接解构拿到关键字段,省去一堆getter调用。
实战建议
说了这么多,总结几条我个人的经验:
- 能用data class就用,别自己手写模板代码
- 主构造函数参数尽量用val,保持不可变
- 类体内不要定义参与equals/hashCode的属性,容易出bug
- copy()是状态管理的好帮手,多用它来创建新状态
- 解构声明让代码更简洁,但注意顺序别搞错
嗯,数据类的基础就这些。看似简单,但用好了能大幅提升代码质量。下一章咱们聊聊不可变架构设计,看看怎么把这些基础特性组合起来,构建健壮的应用。