Kotlin標準函式run with let also apply的區別
阿新 • • 發佈:2018-11-10
Kotlin的Standard.kt中的一些函式很相像,很多時候我們不確定使用哪個。這篇文章我將教大家如何區分他們的不同,以及如何在正確的場景下使用正確的函式
Standard.kt中有 run,T.run,with,T.apply,T.also,T.let幾個函式,他們的共同的功能都是執行程式。但是他們之間還是有區別的。
一共有6個函式。我將根據返回值以及block函式的引數對它歸類
- T.apply和T.also是一對,它們的返回值為this,他們的區別在於apply block引數中傳遞的是this,also block引數中傳遞的是it
- T.run和T.let是一對,它們的返回值是block執行的結果,它們的區別在於run block引數傳遞的是this,let block引數傳遞的是it
- run和with是一對,run block引數中既不是this也不是it。with block引數是this
函式塊
首先我們用run函式來演示下函式塊
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
在函式塊中我們可以重新宣告mood變數
with和T.run對比
with(webview.settings) { javaScriptEnabled = true databaseEnabled = true } // similarly webview.settings.run { javaScriptEnabled = true databaseEnabled = true }
他們兩實現的功能是一樣的。如果webview.setting可能為null
// Yack!
with(webview.settings) {
this?.javaScriptEnabled = true
this?.databaseEnabled = true
}
}
// Nice.
webview.settings?.run {
javaScriptEnabled = true
databaseEnabled = true
}
this vs it引數
stringVariable?.run { println("The length of this String is $length") } // Similarly. stringVariable?.let { println("The length of this String is ${it.length}") }
如果你檢查T.run函式簽名,你會發現T.run它只是作為擴充套件函式呼叫block: T.()。因此,在範圍內,T可以稱為this。在程式設計中,this大多數時候可以省略。因此,在上面的示例中,我們可以 {this.length}。我稱之為傳送此作為引數。
返回this vs 返回other
stringVariable?.let {
println("The length of this String is ${it.length}")
}
// Exactly the same as below
stringVariable?.also {
println("The length of this String is ${it.length}")
}
他們微妙的不同就是他們的返回值。T.let返回的是函式快的返回值Unit,而T.also返回T本身即this。
val original = "abc"
// Evolve the value and send to the next chain
original.let {
println("The original String is $it") // "abc"
it.reversed() // evolve it as parameter to send to next let
}.let {
println("The reverse String is $it") // "cba"
it.length // can be evolve to other type
}.let {
println("The length of the String is $it") // 3
}
// Wrong
// Same value is sent in the chain (printed answer is wrong)
original.also {
println("The original String is $it") // "abc"
it.reversed() // even if we evolve it, it is useless
}.also {
println("The reverse String is ${it}") // "abc"
it.length // even if we evolve it, it is useless
}.also {
println("The length of the String is ${it}") // "abc"
}
// Corrected for also (i.e. manipulate as original string
// Same value is sent in the chain
original.also {
println("The original String is $it") // "abc"
}.also {
println("The reverse String is ${it.reversed()}") // "cba"
}.also {
println("The length of the String is ${it.length}") // 3
}
使用
// Normal approach
fun makeDir(path: String): File {
val result = File(path)
result.mkdirs()
return result
}
// Improved approach
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }
// Normal approach
fun createInstance(args: Bundle) : MyFragment {
val fragment = MyFragment()
fragment.arguments = args
return fragment
}
// Improved approach
fun createInstance(args: Bundle)
= MyFragment().apply { arguments = args }
// Normal approach
fun createIntent(intentData: String, intentAction: String): Intent {
val intent = Intent()
intent.action = intentAction
intent.data=Uri.parse(intentData)
return intent
}
// Improved approach, chaining
fun createIntent(intentData: String, intentAction: String) =
Intent().apply { action = intentAction }
.apply { data = Uri.parse(intentData) }