Kotlin介绍系列(三)高级用法之DataClass

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin介绍系列(三)高级用法之DataClass相关的知识,希望对你有一定的参考价值。

参考技术A 经常会需要创建一些类除了保存数据不干其他事情,比如我们解析网络请求下来的数据。Kotlin就提供了一个非常方便的class—— data class

我们知道在Kotlin中,声明类的同时可以方便的直接声明构造方法等参数,鉴于data class只是存放数据,那么只一个构造方法足矣,所以连类的body也就不需要了。是不是很清爽?

编译器会根据我们在构造函数里声明的属性自动导出下列成员:

经常会遇到我们只需要替换一个对象的个别属性,而其他属性保留的情况。这就是data class中生成的copy函数的作用了。
本文已开始的例子类,它的生成的默认copy函数是下面这样的:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
这就运训我们这样写:

data class的生成的component方法给我们的结构化声明及使用提供了可能

Kotlin介绍系列(三)高级用法之object
Kotlin介绍系列(三)高级用法之Delegation

快速上手 Kotlin 开发系列之作用域函数

本节介绍 Kotlin 中的作用域函数。

作用域函数是什么

作用域函数是 Kotlin 内置的可以对数据做一系列变换的函数。它们与集合的操作符非常的相似,但是集合的操作符只能用于集合的数据变换,而作用域函数可以应用于所有对象,它可以对所有对象做一系列的操作。

在 Kotlin 中常用的作用域函数有五个:

run ...
with(T) ...
let ...
apply ...
also ...

作用域函数的使用

还是以代码示例的形式讲述,开始前先做一个数据类 User:

data class User(var name: String)
fun main() 
    val user = User("Changer0")
    //...

let 与 run

let 与 run 都会返回闭包的执行结果,区别在于 let 有闭包参数,而 run 没有闭包参数,其中 run 可以通过 this 来获取是谁来调用 run 的,也就是说 this 指代了外层的调用对象。这是他们的唯一区别。

val letResult = user.let  user: User -> "let: $user.name" 
println(letResult)
val runResult = user.run "let: $this.name" 
println(runResult)

特别的,对于 let 函数,根据 Kotlin 的 Lambda 规则如果只有一个参数时可以省略参数不写,使用 it 来代替:

val letResult = user.let  "let: $it.name" 

also 与 apply

also 与 apply 都不返回闭包的执行结果,与上面类似,区别在于 also 有闭包参数,而 apply 没有闭包参数。

user.also 
    println("also:$it.name")

user.apply 
    println("apply:$this.name")

那他们闭包的返回值结果是什么呢?

打开该作用域函数的声明发现,其实这个作用域函数是对泛型 T 做的扩展函数,对于 also/apply 返回的都是它本身:

public inline fun <T> T.also(block: (T) -> Unit): T 
    contract 
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    
    block(this)
    return this

也就是说,我们可以连续的去调用这个作用域函数,适合链式操作某个对象:

user.also 
    println("also:$it.name")
.apply 
    println("apply:$this.name")
.name

takeIf 与 takeUnless

takeIf 与 takeUnless 主要是用于判断。

我们看下 takeIf 作用域函数,发现闭包只能返回一个 Boolean 类型的值,并且会根据你传入的 Lambda 表达式的执行结果来做判断,如果执行结果为 true 则返回当前对象,否则会返回 null,这也就是为什么 takeIf 的返回值为 T? 。

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? 
    contract 
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    
    return if (predicate(this)) this else null

通常在使用时,在闭包后面使用 ?. 继续执行该对象不为空时的代码,后面通过 ?: 执行该对象为空时的代码:

user.takeIf  it.name.length > 0?.also  println("name:$it.name")  ?: println("name 为空")

takeUnless 则与 takeIf 完全相反,如果 Lambda 表达式执行结果为 false 返回当前对象,否则返回 null:

public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? 
    contract 
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    
    return if (!predicate(this)) this else null

repeat

repeat 函数在之前已经提到过,其实是对 for-in 形式的循环做的封装,不再做具体的介绍:

public inline fun repeat(times: Int, action: (Int) -> Unit) 
    contract  callsInPlace(action) 

    for (index in 0 until times) 
        action(index)
    

with

查看 with 函数发现与其他作用域函数不同,它不是扩展函数,而是一个顶级的函数,传入参数 receiver 和 闭包 block,返回 receiver 执行闭包的结果,返回值类型为泛型 R。

public inline fun <T, R> with(receiver: T, block: T.() -> R): R 
    contract 
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    
    return receiver.block()

with 一般用于对某个对象的整体赋值,这点在 Android 开发中尤其突出,例如对某个 View 属性的整体赋值,可以使用 with。

with(user) 
    this.name = "Changer1007"

以上就是本节内容,欢迎大家关注~

以上是关于Kotlin介绍系列(三)高级用法之DataClass的主要内容,如果未能解决你的问题,请参考以下文章

快速上手 Kotlin 开发系列之反引号

快速上手 Kotlin 开发系列之运算符重载

快速上手 Kotlin 开发系列之循环

快速上手 Kotlin 开发系列之作用域函数

快速上手 Kotlin 开发系列之集合操作符

快速上手 Kotlin 开发系列之比较对象