/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*
* this作为接收器,返回this
*/
@kotlin.internal.InlineOnly
public inline fun T.apply(block: T.() -> Unit): T { ... }
A:对象的初始化
val person = Person().apply {
name = "Android"
age = 20
}
println(person)
上面的代码也可以写的稍微复杂一点:
val person = Person().apply {
name = "Android"
}.apply {
age = 20
}
println(person)
also
也可以实现同样功能:
val person = Person().also {
it.name = "Android"
it.age = 20
}
显然apply
有一个很大的优势:没有必要使用it
作为限定符,因为上下文对象this
可以省略。使用also
也有“好处”:it
非必须名字,也就是我们可以自定义名字,“增加代码的可读性”。参考文档
B:Builder
样式
data class FooBar(var a: Int = 0, var b: String? = null) {
fun first(aArg: Int): FooBar = apply { a = aArg }
fun second(bArg: String): FooBar = apply { b = bArg }
}
fun main(args: Array) {
val bar = FooBar().first(10).second("foobarValue")
println(bar)
}
/**
* Calls the specified function [block] with `this` value as its argument and returns `this` value.
*
* this作为参数,返回this
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun T.also(block: (T) -> Unit): T { ... }
also
看起来像let
,除了它返回接收器T
作为其结果。
A:在代码块中接收器没有使用,变量赋值,并且打印log
val name = "Android".also {
Log.e("name", "名字是$it")
}
这种情况和使用let
一样的效果:
val name = "Android".let {
Log.e("name", "名字是$it")
}
B:对象的初始化
val person: Person = Person().also {
it.name = "Android"
it.age = 20
}
/**
* Calls the specified function [block] with `this` value as its argument and returns its result.
*
* this作为参数,返回it
*/
@kotlin.internal.InlineOnly
public inline fun T.let(block: (T) -> R): R { ... }
A:替代if (object != null)
语句
val len = string?.let {
println("get length of $it")
// 直接使用,不用it?.lenght,也不会遇到NullPointerException
it.length
} ?: 0
println("Length of $text is $len")
或者
string?.let {
println("get length of $it")
it.length
}
B:限制变量使用范围,在let
里面改变了其值后,外面的也会跟着改变
val str = "Android".let {
println("str = $it")
"Kotlin"
}
// 这个str的值其实是“Kotlin”,不是原始的值“Android”
println(str)
如果想要继续使用原始值,可以使用also
代替let
,这样就不会修改原始值。
val str = "Android".also {
println("str = $it")
it.reversed() // 取反
}
// 还是原始值“Android”
println(str)
/**
* Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
*
* 使用指定的接收器作为其接收器,返回it
*/
@kotlin.internal.InlineOnly
public inline fun with(receiver: T, block: T.() -> R): R { ... }
A:在限定范围内处理一个对象
val s: String = with(StringBuilder("init")) {
append("some").append("thing")
println("current value: $this")
toString()
}
println(s)
效果上可以使用let
替代:
val s: String = StringBuilder("init").let {
it.append("some")
it.append("thing")
println("current value: $it")
it.toString()
}
println(s)
在Android常用的地方是在Adapter
中,常规写法:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.nameTextView.text = mGoodList[position].name
holder.specTextView.text = mGoodList[position].spec
holder.priceTextView.text = mGoodList[position].price
holder.numberTextView.text = mGoodList[position].count
holder.itemView.tag = position
}
使用with
函数
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(mGiftCommodityList[position]) {
holder.nameTextView.text = name
holder.specTextView.text = spec
holder.priceTextView.text = price
holder.numberTextView.text = count
holder.itemView.tag = position
}
}
B:使用类的成员扩展函数
object Foo {
fun printString(str: String){
println(str)
}
}
fun main(args: Array) {
// 只能在Foo上下文中调用
with(Foo) {
printString("Kotlin")
}
}
如果要对特定扩展函数进行有意义的分组,则特别推荐使用此策略。
/**
* Calls the specified function [block] with `this` value as its receiver and returns its result.
*
* this作为接收器,返回it
*/
@kotlin.internal.InlineOnly
public inline fun T.run(block: T.() -> R): R { ... }
let
的替代方法。
A:替代if (object != null)
语句
val len = string?.run {
println("get length of $this")
length
} ?: 0
println("Length of $text is $len")
或者
string?.run {
println("get length of $this")
length
}
返回本身 | 返回任意值(最后一个表达式为返回值) | |
---|---|---|
it | also | let |
this | apply | run、with |
实现相同的功能:
// with可以
with(webview.settings) {
javaScriptEnabled = true
databaseEnabled = true
}
// run也没问题
webview.settings.run {
javaScriptEnabled = true
databaseEnabled = true
}
但是当webview.setting可能为null
时,我们再对比一下:
// with看上就不是很好
with(webview.settings) {
this?.javaScriptEnabled = true
this?.databaseEnabled = true
}
// run没问题
webview.settings?.run {
javaScriptEnabled = true
databaseEnabled = true
}
所以在此类情况(当前对象存在可为null
)建议使用run
扩展函数。
run
可以进行链式编写:
val str = "Android".run {
println("str = $this")
"Kotlin"
}.run {
println("str = $this")
"Java"
}
原因是run
函数是扩展函数,它还有一个T
类型的隐式参数,因此参数类型是相同的。功能的主体也实际上是相同的。参考文档
// also
val name = "Android".also {
Log.e("name", "名字是$it")
}
// let
val name = "Android".let {
Log.e("name", "名字是$it")
}
上述两种写法效果是一样的,但是T.also
返回的是T
本身,而T.let
返回的是任意类型的值。这样两者对链式函数的编写就会有不同的功能:T.let
可以对不同的对象进行操作,而T.also
可以对同一个对象进行处理。
val originalString = "Kotlin"
originalString.let {
println("originalString is $it") // "abc"
it.reversed()
}.let {
println("reverseString is $it") // "cba"
it.length
}.let {
println("length is $it") // 3
}
// 同一个值,同样的结构使用also,就不会得到我们想要的结果
originalString.also {
println("originalString is $it") // "abc"
it.reversed() // 取反
}.also {
println("reverseString is ${it}") // "abc"
it.length
}.also {
println("length is ${it}") // "abc"
}
// 为了实现同样的效果进行修改
original.also {
println("originalString is $it") // "abc"
}.also {
println("reverseString is ${it.reversed()}") // "cba"
}.also {
println("length is ${it.length}") // 3
}
两种不同的功能,根据自己的使用场景进行选择,例如:有一个成绩单的List
。
List
。这些都是对原始数据List
进行操作,所以建议使用T.also
链式函数。List
,这种情况就是在男生的基础上再次筛选分数低于60的,并且还打瞌睡,所以建议使用T.let
链式函数。LIst
,这种是首先在男生的基础上,然后再分别筛选,这就需要T.let
和T.also
组合了。组合的例子:
// 普通编写
fun makeDir(path: String): File {
val result = File(path)
result.mkdirs()
return result
}
// 组合链式编写
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }
// 可变list
val personList = mutableListOf()
// age大于50的人数
var count = 0
// add一个第一个对象【apply】
personList.add(Person().apply {
name = "Kotlin"
age = 60
myCar = Car().apply {
name = "宝马"
price = "100万"
number = "12345678"
engine = Engine()
}
})
// add第二个对象【apply】
personList.add(Person().apply {
name = "Kotlin"
age = 30
myCar = Car().apply {
name = "宝马"
price = "100万"
number = "12345678"
engine = Engine()
}
})
// for循环【forEach】
personList.forEach {
// 判断当前person对象是否为null【let】
it.myCar?.let { car ->
// 修改当前person的car的engine信息【with(在限定范围内处理对象)】
with(car.engine) {
type = "宝马123"
date = "2019.9.9"
producer = "china"
}
}
// 判断person的age是否大于50,返回人数【run】
count = it.run {
if (age > 50) count + 1 else count
}
// 最后没有做任何处理只是打印log【also】
}.also {
Log.e("Kotlin", personList.toString())
}
作用
概念
对比