一、“with”函数
- 示例一:构建字母表
// 定义
fun alphabet(): String {
val result = StringBuilder()
for(letter in 'A' .. 'Z') {
result.append(letter)
}
result.append("\nNow I know the alphabet!")
return result.toString()
}
// 测试
>>> println(alphabet())
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Now I know the alphabet!
上述例子中,调用result实例上好几个不同的方法,而且每次调用都要重复result这个名称。若实例名比较长,就比较糟糕!
- 使用***
with***构造字母表
fun alphabet(): String {
val stringBuilder = StringBuilder()
// 指定接受者的值,你会调用它的方法
return with(stringBuilder) {
for(letter in 'A' .. 'Z') {
// 通过显式的“this”来调用接受者值的方法
this.append(letter)
}
// 省略“this”也可以调用方法
append("\nNow I know the alphabet!")
// 从lambda返回值
this.toString()
}
}
with结构看起来像是一种特殊的语法结构,但它实际上是一个接受两个参数的函数:这个例子中两个参数分别是stringBuilder和一个lambda。这里利用了把lambda放在括号外的约定,这样整个调用看起来就像是内建的语言功能。当然也可以把它写成*with(stringBuilder, { ... })***,但可读性就会差很多。
with函数把它的第一个参数转换成作为第二个参数传给它的lambda的接受者。可以显示地通过this引用来访问这个接受者。或者,按照惯例,可以省略this引用,不用任何限定符直接访问这个值的方法和属性。
- 重构:使用with和一个表达式函数体来构建字母表
fun alphabet() = with(StringBuilder()) {
for(letter in 'A' .. 'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
toString()
}
现在这个函数只返回一个表达式,所以使用表达式函数体语法重写了它。可以创建一个新的StringBuilder实例直接当作实参传给这个函数,然后在lambda中不需要显示的this就可以引用这个实例。
注意:
with返回的值是执行lambda代码的结果,该结果就是lambda中的最后一个表达式(的值)**。
二、“apply”函数
apply*函数几乎和with函数一模一样,唯一的区别是**apply始终会返回作为实参传递给它的对象*(换句话说,接受者对象)。
- 使用apply构建字母表
fun alphabet() = StringBuilder().apply {
for(letter in 'A' .. 'Z')
append(letter)
append("\nNow I know the alphabet!")
}.toString()
***apply被声明成一个扩展函数。它的接受者变成了作为实参的lambda的接受者。执行apply***的结果是StringBuilder,所以接下来你可以调用toString把它转换成String。
许多情况下***apply***都很有效:
- 在创建一个对象实例并需要用正确的方式初始化它的一些属性时。在Java中,这通常是通过另外一个单独的Builder对象来完成的;而在Kotlin中,可以在任何对象上使用***
apply***,完成不需要任何来自自定义该对象的哭的特殊支持。
fun createViewWithCustomAttributes(context: Context) =
TextView(context).apply {
text = "Sample Text"
textSize = 20.0F
setPadding(10, 0, 0, 0)
}