高阶扩展函数
1.let函数
let的使用
在闭包内用it代替调用者,let的返回值为函数的最后一行。
let常用来做为判空后的处理
//不用let时每次都需要对textView判空
textView?.text = "let"
textView?.setTextColor(Color.BLACK)
textView?.ellipsize = TextUtils.TruncateAt.END
textView?.let {
it.text = "let"
it.setTextColor(Color.BLACK)
it.ellipsize = TextUtils.TruncateAt.END
}
let的定义
//定义两个泛型T,R,T.let代表对任意类T添加let扩展函数,这个扩展函数的返回值为R
//参数block是一个函数且类型为(T) -> R
//由于let返回值为R且block返回值也为R,所以直接返回block函数的返回值
//调用let的是T,所以let闭包里this就代表T,block的参数为T,block应传入this,所以在block闭包内使用it即代表T
public inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
上面可能有些人看不太明白,我们把泛型去了再看会发现其实挺简单的
这里定义String的扩展函数let1,返回Int类型
//去掉泛型后的let
public inline fun String.let1(block: (String) -> Int): Int {
return block(this)
}
我们分别调用let1和let其结果是一样的
//这里给出调用let1时的简化过程,对函数有不太理解的可以去看看上一篇文章
var a: Int
a = "1".let1(fun(a: String): Int {
return a.toInt()
})
Log.i("kotlin fun test2: ", "返回值$a")
a = "1".let1({ a ->
a.toInt()
})
Log.i("kotlin fun test2: ", "返回值$a")
a = "1".let1 { a ->
a.toInt()
}
Log.i("kotlin fun test2: ", "返回值$a")
//这里就是我们熟悉的let调用方式
a = "1".let1 {
it.toInt()
}
Log.i("kotlin fun test2: ", "返回值$a")
//然后我们直接调用let
a = "1".let {
it.toInt()
}
Log.i("kotlin fun test2: ", "返回值$a")
以上的几个函数结果都是相同的
2.run函数
相对于let,我更喜欢使用run,一般情况下let能做的事用run也可以做,而且用run代码会更简洁。
run的使用
在闭包内用this代替调用者,run的返回值为函数的最后一行。
//相对于let,代码简洁
textView?.run {
text = "run"
setTextColor(Color.BLACK)
ellipsize = TextUtils.TruncateAt.END
}
run的定义
相对于let,run的改动点只有一处,由(T)换成了T.()
//定义两个泛型T,R,T.run代表对任意类T添加run扩展函数,这个扩展函数的返回值为R
//参数block是一个函数且类型为T.() -> R
//由于run返回值为R且block返回值也为R,所以直接返回block函数的返回值
//T.()代表给T添加一个匿名扩展函数,即block函数是T的匿名扩展函数,所以block函数闭包内的this即代表T
public inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}
上面注释前三行和let一样,只有最后一行有区别,这也是导致let和run差异的原因。
3.with函数
with函数在实际项目中应用很少,基本都被run代替,这里还是介绍下with函数的原理
with的使用
with用法和let、run有所不同,with不是作为扩展函数使用的,with接收一个对象,闭包内this代表传入的对象,with的返回值为函数的最后一行。
with(textView) {
if (this != null) {
text = "with"
setTextColor(Color.BLACK)
ellipsize = TextUtils.TruncateAt.END
}
}
with的定义
//with和run的区别在于,run是通过扩展函数实现,with通过顶层函数实现(不借助类或对象可以直接使用)
//由于with是顶层函数,闭包内不包含上下文,所以调用block函数时需要指定T的对象进行调用,这也导致了with相对于run需要多传入一个参数T
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
4.apply函数
apply和run都是项目中最常见的扩展函数,run用来返回闭包内最后一行,apply用来返回调用者对象。
apply的使用
apply最常用的有两个地方
1.给对象进行初始化赋值
2.给model进行数据判空处理
//1
var textView = findViewById<TextView>(R.id.btn).apply {
text = "let"
setTextColor(Color.BLACK)
ellipsize = TextUtils.TruncateAt.END
}
//2
userInfo?.apply {
//userInfo不为空时要处理的内容
}?.info?.apply {
//info不为空时要处理的内容
}?.title?.apply {
//title不为空时要处理的内容
}
class UserInfo {
var name: String? = null
var info: Info? = null
class Info {
var title: String? = null
}
}
apply的定义
//相较于run,apply只有一个泛型,即apply是T的扩展函数,返回的也是T
//block依旧为T的匿名扩展函数,apply闭包包含T的上下文关系,直接在闭包调用block,返回this
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
5.also函数
also与with一样,在实际项目中应用较少,这里也只简单介绍下
also的使用
textView?.also {
it.text = "also"
it.setTextColor(Color.BLACK)
it.ellipsize = TextUtils.TruncateAt.END
}
also的定义
//also是T的扩展函数,所以also的闭包的上下文是T
//匿名函数block的入参为T,所以block传入this
//block是非扩展函数,所以block的闭包上下文为also的外层对象,在block的闭包里用it指向T
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}