1 介绍
1.1 定义

1.2 常用高阶函数
More details see scope functions
let() / run()
- Declaration
/**
* Calls the specified function [block] with `this` value as its argument and returns its result.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#let).
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
/**
* Calls the specified function [block] with `this` value as its receiver and returns its result.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#run).
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
let() has a functional type of arg (T)->R, and it returns R.
run() has a functional type of arg T.()->R, and it returns R.
- Invoke
class Person(var name: String, var age: Int){
override fun toString(): String {
return "${this.name} is ${this.age} years old."
}
}
fun main() {
var person0 = Person("Pony", 24)
println(person0)
val person1 = person0.let {
it.name = "Jack"
it.age = 55
it
}
println(person0)
println(person1)
val person2 = person1.run {
this.name = "Robin"
this.age = 50
this
}
println(person1)
println(person2)
}
Output in console:

In conclusion:
- Similarity:
let() and run() are almost the same, they both can reach the object which invoke them and they both return the result of lambda expression;
- Contrast:
let() treats the object as an argment of the block, and reaches the object by using it;
run() treats the object as a receiver, and reaches the object by using this or even this can be omitted.
- So:
It is recommended by the official that when the block aims mainly at operating the object, run() should be choosen, otherwise let().
also() / apply()
- Declaration
/**
* Calls the specified function [block] with `this` value as its argument and returns `this` value.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#also).
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
also() has a functional type of arg (T)->Unit and returns T;
apply() has a functional type of arg T.()->Unit and returns T;
- Invoke
class Person(var name: String, var age: Int){
override fun toString(): String {
return "${this.name} is ${this.age} years old."
}
fun selfIntroduction(){
println(this)
}
}
fun main() {
val person = Person("Pony", 50)
person.also {
println(it.name)
println(it.age)
}.selfIntroduction()
person.apply {
this.name = "Jack"
this.age = 55
}.selfIntroduction()
}
Output in console:

In conclusion:
- Similarity:
also() and apply() are almost the same, they both can reach the object which invoke them and they both return the object itself;
- Contrast:
also() treats the object as an argment of the block, and reaches the object by using it;
apply() treats the object as a receiver, and reaches the object by using this or even this can be omitted.
- So:
It is recommended by the official that when the block aims mainly at operating the object, apply() should be choosen, otherwise also().
use()
- Declaration
/**
* Executes the given [block] function on this resource and then closes it down correctly whether an exception
* is thrown or not.
*
* @param block a function to process this [Closeable] resource.
* @return the result of [block] function invoked on this resource.
*/
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
}
use() is defined for Closeable, which needs to be closed after been used. Such as io stream etc.
use() has a functional type of arg (T)->R where T must be a Closeable?, and returns R.
- Invoke
fun main() {
File("build.gradle")
.inputStream()
.reader()
.buffered()
.use {
it.readLines().forEach { eachLine ->
println(eachLine)
}
}
}
Output in console:

In conclusion:
use() is used to close those Closeable? automatically.
1.3 高阶函数的调用
对于forEach()高阶函数,常规调用应该写作:

如果函数类型的参数是最后一个传入参数,那么在调用高阶函数的时候,这个函数类型的参数可以连大括号一起,写到小括号外面去。

如果函数类型的参数是唯一一个传入参数,那么在调用的时候,函数类型的参数已经写到小括号外面了,小括号里面什么都没有,可以去掉,而且推荐去掉,这为后面的DSL语法打下了基础。

2 实战
2.1 计时函数
fun cost(block: () -> Unit) {
val start: Long = System.currentTimeMillis()
block()
println("${System.currentTimeMillis() - start} ms")
}
这个函数很好理解,不赘述。
2.2 斐波拉契函数
fun fiboracci(): () -> Long {
var first = 0L
var second = 1L
return {
val next = first + second
val current = first
first = second
second = next
current
}
}
这个斐波拉契函数并不是最终用于计算斐波拉契数列的函数,其输出的lambda表达式才是重要的被调用者,这个斐波拉契函数只是给定了斐波拉契数列的前两个值,仅此而已。
2.3 主函数
fun main() {
cost {
val fiboracciNext = fiboracci()
for (i in 0..10) {
println(fiboracciNext())
}
}
}
主函数中打印的始终是lambda表达式的输出,即最后一行的值,也就是程序中变量current的值。
2.4 控制台输出

小结
