原链接出处

scope functions

Kotlin “scope functions” 允许改变一个变量的 scope,或者 range (作用域范围). 在此在Kotlin standard library中有五个类似方法: apply, run, with, letalso
run 为例说明:

  1. fun myFun() {
  2. val outside = 6.2831853071
  3. run {
  4. val inside = 1.61803398875
  5. // outside和inside变量在此域范围能使用
  6. }
  7. // 只有outside能在此域范围使用
  8. }

‘this’ is the receiver

针对 scope functions 中的 apply run with , 已个很重要的功能就是 在代码块中被 this 引用的对象是一个被用在调用中的变量(the object referred to by this inside the block is the variable that’s used in the call)

class Foo {
    //...
    myView.run {
        // this是myView的引用,而不是 Foo对象引用 
        alpha = 0.5f
        background = ContextCompat.getDrawable(context, R.drawable.my_drawable)
    }
}

因为 this 的 scope(域范围)已经变成了 run 代码块中的 myView ,如果想获取改变前的 this 对象,可以通过 this@Foo 去获取

Three, .. Two values

既然 scope functions 是 functions, 就应该有返回值, 可能的返回值如下:

  • The object itself

  • The last value of the block

  • N̶o̶t̶h̶i̶n̶g̶

apply

value第一种情况就是返回对象, 也就是receiver自己, 跟buidler很像. apply就是这样工作.

val paint = Paint().apply {
    color = Color.MAGENTA
    style = Paint.Style.STROKE
    textSize = textHeadlinePx
}

run/with

第二种情况就是 function 类型, 返回值为代码块的最后一个表达式, 也就是run and with工作机制.

val line = PoetryGenerator.obtain().run {
    style = "Emily Dickinson"
    style += "Lucille Clifton"
    lines = 1
    generate()
}

line等于 generate() 此方法的返回值,如果假设返回可为null的话,返回值为this?.generate(), 然而的大部分使用方式都是如下:

with (myConfig) { //用于myConfig的配置,而不关心返回值
    data = value
    autoRefresh = false
    // ...etc...
}

I’d rather be ‘it’

在很多情况下将 this 临时转换成其他对象引用挺方便,但是如下情况去显示很奇怪:

myIntent?.run { 
    data = this@MainActivity.data
    startActivity(this)
}

this 在代码块中是 myIntent的引用,因此 startActivity(this) 看起来挺奇怪的,更舒服的方式应该是 startActivity(intent).

因此引入了 letalso scope function, 在此场景中,我们实质上想检查 myIntent 不为null时执行代码块中逻辑. Kotlin中针对此场景一贯的处理方式是用let scope function:

myIntent?.let {
    it.data = data
    startActivity(it)
}

letrun 的作用相同,除了对象被 this 引用改成了被 it 引用
完整的写法如下:

myIntent?.let { intent ->
    intent.data = data
    startActivity(intent)
}

also 的作用方式和 apply 相似,也只是对象被 this 引用改成了被 it 引用

also 很实用有两个主要的原因:

  • it 可以被认为用做其名字:创建一个对象并且用it操作
val myListener = Listener().also {
    addListener(it)
}
  • 当与不相关联的对象和表达式协作时,优势更加明显(可以在不改变原本代码的前提下实现功能)
val key: String get() = keystore.getKey(KEY_ID).also {
    Log.v(TAG, "Read key at ${System.currentTimeMillis()}")
}

How do I choose?

如果想返回开始的对象, 选择 applyalso. 如果想返回方法中的值,选择 let, run, 和 with.

Scope Functions - 图1