obj.apply{ func1() } 的结果不同于 obj.func1()

Result of obj.apply{ func1() } is different than obj.func1()

在kotlin中,apply{}定义为inline fun <T> T.apply(block: T.() -> Unit): T
我以为使用这个函数是为了最小化这段代码

obj.func1()
obj.func2()
obj.func3()
...

obj.apply {
    func1()
    func2()
    func3()
}

但现在我认为它有一些完全不同的实际用例。在我的 android 工作室项目中,我得到了这个日志

2020-12-08 13:3:22.648 28006-28006/com.skb.skara D/VideoViewActivity: onStart: pos 2677
2020-12-08 13:3:22.649 28006-28006/com.skb.skara D/VideoViewActivity: initializePlayer: seeked to 0
2020-12-08 13:3:22.650 28006-28006/com.skb.skara D/VideoViewActivity: onStart: pos 2677

对于此代码

private var currentPosition: Int

override fun onStart() {
    super.onStart()
    Log.d(TAG, "onStart: pos $currentPosition")
    initializePlayer()
    Log.d(TAG, "onStart: pos $currentPosition")
    if (currentPosition == 0) {
        Log.d(TAG, "onStart: vid start")
        fullscreen_video.start()
    }
    show()
}

private fun initializePlayer() {
    fullscreen_video.apply {
        setVideoPath(videoUrl)
        seekTo(currentPosition)
        Log.d(TAG, "initializePlayer: seeked to $currentPosition")
    }
}

然后我把initialisePlayer函数改成了这个

private fun initializePlayer() {
    fullscreen_video.setVideoPath(videoUrl)
    fullscreen_video.seekTo(currentPosition)
    Log.d(TAG, "initializePlayer: seeked to $currentPosition")
}

然后我得到了这个日志

2020-12-08 13:12:53.548 28006-28006/com.skb.skara D/VideoViewActivity: onStart: 2677
2020-12-08 13:12:53.550 28006-28006/com.skb.skara D/VideoViewActivity: initializePlayer: seeked to 2677
2020-12-08 13:12:53.550 28006-28006/com.skb.skara D/VideoViewActivity: onStart: 2677

有人请解释这个行为并解释作用域函数的使用apply{}

看下面的table,范围内也是,this.currentPositionfullscreen_video.currentPosition,而不是你的var currentPosition

Kotlin 的作用域运算符就像一把瑞士军刀,每个场景都有一个方法。

https://kotlinlang.org/docs/reference/scope-functions.html

Function Object reference Return value Is extension function
let it Lambda result Yes
run this Lambda result Yes
run - Lambda result No: called without the context object
with this Lambda result No: takes the context object as an argument.
apply this Context object Yes
also it Context object Yes

它对于 DSL API 以及在创建一个简单的 Java 对象后调用多个 setter 非常有用。

    val x = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply {
      init(null as KeyStore?)
    }

在这种形式中,不完整的对象在完全构造之前永远不可用。所以可以直接赋值给一个字段(忽略并发和内存壁垒)。

而且还保持表达式形式的内容,因此您可以链接运算符,而不是强制改变值。

这是一个示例,其中 also 很有用。

fun getRandomInt(): Int {
    return Random.nextInt(100).also {
        writeToLog("getRandomInt() generated value $it")
    }
}

如果您切换到 also,它应该会更清楚地说明您的情况。

小心,VideoView有自己的currentPosition。在其 apply 块内访问 currentPosition 将获得 VideoView.

的 currentPosition
private fun initializePlayer() {
    fullscreen_video.apply {
        setVideoPath(videoUrl)
        seekTo(currentPosition) // This will use VideoView.currentPosition
        Log.d(TAG, "initializePlayer: seeked to $currentPosition") // This, too
    }
}

关于apply的解释:

The context object is available as a receiver (this). And this always can be omitted.

如果要访问上面声明的 currentPosition,请使用 this@YourActivity.currentPosition。将 YourActivity 替换为您的 activity.