一直对这几个函数有些迷惑,一会用这个,一会用那个,这次好好弄明白一下!
(1)源码解析
public inline fun T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
contract
是 Kotlin 用于定义函数契约的一种机制。函数契约可以向编译器提供关于函数行为的一些额外信息,帮助编译器进行更准确的代码分析、优化以及处理一些边缘情况等。callsInPlace(block, InvocationKind.EXACTLY_ONCE)
这个语句表示向编译器声明,传入的函数参数 block
在 also
函数执行过程中会被调用,并且恰好调用一次(InvocationKind.EXACTLY_ONCE
表示精确地调用一次)。这样的契约声明可以让编译器在一些涉及到函数参数是否被调用、调用次数等相关的代码检查和优化时有更明确的依据,例如,在某些内联函数展开的场景下,对于这种有调用次数约定的函数参数,编译器可以更好地处理其相关的逻辑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).let {
it.name = "xiaoming"
it.printFun() // name: xiaoming, age: 25
"hello"
}
println(person) // hello
}
不返回本身,返回传入的函数的最后一行。
(1)源码解析
public inline fun T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
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调用
(1)源码解析
public inline fun T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
(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",是返回的对象本身
(1)源码解析
public inline fun T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
(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
}
与前面四个不一样了,前面四个都是函数的扩展函数,而with确实一个原型函数,是直接调用的
(1)源码解析
public inline fun with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
(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
}
这几个函数都是操纵某个对象的函数,但操作方式以及返回值有差别。
函数的扩展函数:let(it调用,返回传入函数的返回值)、also(it调用,返回对象本身)、apply(this调用,返回对象本身)、run(this调用,返回传入函数的返回值)
原型函数:with(需传入对象,this调用,返回传入函数的返回值)