一直对这几个函数有些迷惑,一会用这个,一会用那个,这次好好弄明白一下!
1 let
(1)源码解析
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
- 对象T的扩展函数,且为内联函数,减少函数调用开销。
contract
是 Kotlin 用于定义函数契约的一种机制。函数契约可以向编译器提供关于函数行为的一些额外信息,帮助编译器进行更准确的代码分析、优化以及处理一些边缘情况等。callsInPlace(block, InvocationKind.EXACTLY_ONCE)
这个语句表示向编译器声明,传入的函数参数block
在also
函数执行过程中会被调用,并且恰好调用一次(InvocationKind.EXACTLY_ONCE
表示精确地调用一次)。这样的契约声明可以让编译器在一些涉及到函数参数是否被调用、调用次数等相关的代码检查和优化时有更明确的依据,例如,在某些内联函数展开的场景下,对于这种有调用次数约定的函数参数,编译器可以更好地处理其相关的逻辑block(this)
调用传入的函数block
,并且将当前对象的实例T作为参数传递给block
。- 最后返回block函数的运行结果
(2)用法
主要用来判空,需使用it,且返回传入函数的返回
class Person(var name: String, var age: Int) {
fun printFun() {
println("name: $name, age: $age")
}
}
fun main() {
val person = Person("xiaohong", 25).let {
it.name = "xiaoming"
it.printFun() // name: xiaoming, age: 25
"hello"
}
println(person) // hello
}
不返回本身,返回传入的函数的最后一行。
2 also
(1)源码解析
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
- 对象T的扩展函数,且为内联函数,减少函数调用开销。
block(this)
调用传入的函数block
,并且将当前 对象的实例T作为参数传递给block
。- 最后返回对象本身
(2)用法
要使用it访问
class Person(var name: String, var age: Int) {
fun printFun() {
println("name: $name, age: $age")
}
}
fun main() {
val person = Person("xiaohong", 25).also {
it.name = "xiaoming"
it.printFun() // name: xiaoming, age: 25
}
person.printFun() // name: xiaoming, age: 25
}
可见,最后返回了对象本身 ,且使用时需要用it调用
3 apply
(1)源码解析
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
- 同also一样,返回了对象本身
- ⚠️注意一点,这里的block是T对象的扩展函数,即T.(),这样也是可直接在block函数中访问T对象中的数据,而无需it
(2)用法
无需it访问,且返回对象本身
class Person(var name: String, var age: Int) {
fun printFun() {
println("name: $name, age: $age")
}
}
fun main() {
val person = Person("xiaohong", 25).apply {
name = "xiaoming"
printFun() // name: xiaoming, age: 25
"hello"
}
person.printFun() // name: xiaoming, age: 25
println(person) // Person@3ac3fd8b
}
可见,没有使用it,直接访问的name属性,且没返回"hello",是返回的对象本身
4 run
(1)源码解析
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
- 看源码可知,与apply的差别主要在于返回的东西,apply是返回对象本身,run是返回传入函数的返回,或者说传入函数的最后一行
(2)用法
与apply一样,既然传入的是对象的扩展,那么也不需要it访问,可直接访问对象属性
class Person(var name: String, var age: Int) {
fun printFun() {
println("name: $name, age: $age")
}
}
fun main() {
val person = Person("xiaohong", 25).run {
name = "xiaoming"
printFun() // name: xiaoming, age: 25
"hello"
}
println(person) // hello
}
5 with
与前面四个不一样了,前面四个都是函数的扩展函数,而with确实一个原型函数,是直接调用的
(1)源码解析
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
- with不是T.with,因此不是扩展函数
- 有两个参数,一个为传入对象T,还有一个是传入的函数参数,或者说lambda表达式,其是做为T对象的扩展函数
- 返回传入函数的返回值
(2)用法
直接使用with,先传入对象,再传入对T操作的lambda表达式,最后返回lambda表达式的返回值
class Person(var name: String, var age: Int) {
fun printFun() {
println("name: $name, age: $age")
}
}
fun main() {
val person = with(Person("xiaohong", 25)) {
name = "xiaoming"
printFun() // name: xiaoming, age: 25
"hello"
}
println(person) // hello
}
6 总结
这几个函数都是操纵某个对象的函数,但操作方式以及返回值有差别。
函数的扩展函数:let(it调用,返回传入函数的返回值)、also(it调用,返回对象本身)、apply(this调用,返回对象本身)、run(this调用,返回传入函数的返回值)
原型函数:with(需传入对象,this调用,返回传入函数的返回值)