前言
上篇讲的泛型、扩展函数、内联函数是为了理解常用的高阶函数打下基础, let/also/with/run/apply/repeat 都是定义在Standard.kt 文件里,熟练掌握这些函数的使用可以大大提高我们的编码效率。接下来对这些高阶函数进行彻底分析。
通过本篇文章,你将了解到:
1、let 原理与使用
2、also 原理与使用
3、with 原理与使用
4、run 原理与使用
5、apply 原理与使用
6、repeat 原理与使用
7、总结
为方便演示,先定义一个学生信息的基础类:
class StudentInfo {
//姓名
var name:String? = "Fish"
var alias:String ? = "小鱼人"
//省份
var province:String? = "北京"
//年龄
var age:Int ? = 18
//性别
var isBoy:Boolean = true
//分数
var score:Float = 88f
}
1、let 原理与使用
原理
public inline fun <T, R> T.let(block: (T) -> R): R {
//内联函数,扩展函数,定义了泛型,接收一个函数类型参数
//调用函数,返回执行结果
return block(this)
}
定义了泛型T,let 作为T的扩展函数,let 参数为函数类型,接收T对象,返回R。
使用
不使用let
fun testLet1(studentInfo: StudentInfo) {
studentInfo?.isBoy = false
studentInfo?.name = "小鱼人"
studentInfo?.age = 14
}
使用let
fun testLet2(studentInfo: StudentInfo) {
var letRet = studentInfo?.let {
it.isBoy = false
it.name = "小鱼人"
it.age = 14
//Lambda结果作为let 返回值
"Fish"
}
println("letRet:$letRet")
}
可以看出,简化了操作。因为let 里的函数类型只有一个参数,所以可以用it指代该参数(Lambda的约定)。
使用let 一是可以约束变量的操作范围,二是可以在入口处统一判空。
2、also 原理与使用
原理
public inline fun <T> T.also(block: (T) -> Unit): T {
//扩展函数,函数类型入参为T对象,返回T对象
block(this)
//返回调用者本身
return this
}
与let 类似,只是返回值有点差异。
使用
不使用also
fun testAlso1(studentInfo: StudentInfo) {
studentInfo?.isBoy = false
studentInfo?.name = "小鱼人"
studentInfo?.age = 14
}
使用also
fun testAlso2(studentInfo: StudentInfo) {
var letRet = studentInfo?.also {
it.isBoy = false
it.name = "小鱼人"
it.age = 14
//Lambda结果未被使用
"Fish"
}
println("alsoRet:${letRet.name}")
}
also 返回值为调用者本身,也就是studentInfo,因此我们可以继续使用studentInfo进行操作。
fun testAlso3(studentInfo: StudentInfo) {
studentInfo?.also {
it.isBoy = false
it?.name = "小鱼人"
it?.age = 14
//Lambda结果作为let 返回值
"Fish"
}.let {
//继续调用
it.score = 99f
}
}
also 原理、作用与let 类似,因为其返回对象本身,因此可以用在链式调用的场景。
3、with 原理与使用
原理
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
//内联函数,带两个参数,一个是接收者,另一个函数类型
//T.() 泛型T的扩展函数,函数无参数
//返回接收者调用结果,也就是Lambda返回值
return receiver.block()
}
需要注意的是:with 并不是扩展函数,因此无需使用对象访问它。
with 反编译结果:
public static final Object with(Object receiver, @NotNull Function1 block) {
//传入接收者对象
return block.invoke(receiver);
}
T.() 表示T的扩展函数,可以表示为:block:T.()->R,可以使用receiver.block() 访问,最终实现block的函数体(Lambda)里持有T的对象,因此内部可以使用this访问T的属性和函数。
使用
不使用with
fun testWith1(studentInfo: StudentInfo) {
studentInfo?.isBoy = false
studentInfo?.name = "小鱼人"
studentInfo?.age = 14
}
使用with
fun testWith2(studentInfo: StudentInfo) {
var withRet = with(studentInfo) {
isBoy = false
name = "小鱼人"
age = 14
"Fish"
}
println("withRet:$withRet")
}
可以看出,使用with时只需要传入要操作的对象,而Lambda表达式里即可直接操作属性和函数。
with 本质上是通过扩展接收者函数,内部就可以访问属性和函数(隐藏了this),通常用在需要多次书写对象调用的场景,比如ViewHolder 访问View。
with 有个弊端:
因为不是扩展函数,因此无法像let/also 一样在调用时通过"?"判空。
4、run 的原理与使用
原理
public inline fun <T, R> T.run(block: T.() -> R): R {
//扩展函数,函数类型也扩展了T
return block()
}
run与with 类似,同样的是扩展T.(),因此在block里能够访问属性和函数。
使用
不使用run
//run 使用
fun testRun1(studentInfo: StudentInfo) {
studentInfo?.isBoy = false
studentInfo?.name = "小鱼人"
studentInfo?.age = 14
}
使用run
fun testRun2(studentInfo: StudentInfo) {
var withRet = studentInfo?.run {
isBoy = false
name = "小鱼人"
age = 14
"Fish"
}
println("withRet:$withRet")
}
可以看出,run 比 with 多了可以判空的功能,并且比let 多了可以省略it访问的功能,因此:
run 结合了let 与 with 的功能,它俩能做的run 也能做。
5、apply 的原理与使用
原理
public inline fun <T> T.apply(block: T.() -> Unit): T {
//扩展函数
block()
//返回调用者本身
return this
}
和run 相似,只是apply 返回值为对象本身。
使用
不使用apply
fun testApply1() {
var studentInfo = StudentInfo()
studentInfo.isBoy = false
studentInfo.name = "小鱼人"
studentInfo.age = 14
}
使用apply
fun testApply2() {
var applyRet = StudentInfo().apply {
isBoy = false
name = "小鱼人"
age = 14
"Fish"
}
println("withRet:${applyRet.name}")
}
apply 返回对象本身,因此我们可以在Lambda里做一些初始化操作。
当Lambda 执行完毕后,返回的对象已经初始化完毕。
apply 多用于对象初始化过程以及链式调用。
6、repeat 的原理与使用
原理
public inline fun repeat(times: Int, action: (Int) -> Unit) {
//循环调用action,传入参数为当前次数
for (index in 0 until times) {
action(index)
}
}
repeat 不是扩展函数。
使用
fun testRepeat2() {
var list = mutableListOf<StudentInfo>()
repeat(10) {
//重复这个动作10次
list.add(StudentInfo())
println("第 $it 个")
}
}
7、总结
let/also/with/run/apply/repeat 等高阶函数都已经分析完毕,用图总结:
至此,Kotlin 函数的主要内容分析完毕,下篇将开启Kotlin 类与对象系列。
本文基于Kotlin 1.5.3,文中Demo请点击
您若喜欢,请点赞、关注,您的鼓励是我前进的动力
网友评论