[英]Kotlin: How can I invoke a lambda field which has a generic type of its class?
[英]How can I invoke a function that returns a lambda which also accepts another lambda as its parameter ( () -> Unit ) -> Unit in Kotlin?
代碼 A 來自 Roman Y 回答的問題。
代碼A在調用background(appState)() {...}
時可以正常工作,為什么我不能去掉括號()?
但是代碼 B 在使用background(appState) {...}
調用時失敗了,為什么?
還有更多代碼 C 在調用時可以正常運行val aa=background(appState) aa{...}
,為什么?
代碼A
@Composable
fun NiaApp(
windowSizeClass: WindowSizeClass,
appState: NiaAppState = rememberNiaAppState(windowSizeClass)
) {
NiaTheme {
background(appState)() {
Scaffold(
...
) { padding ->
}
}
}
}
@Composable
fun background(appState: NiaAppState): @Composable (@Composable () -> Unit) -> Unit =
when (appState.currentDestination?.route) {
ForYouDestination.route -> { content ->
NiaGradientBackground(content = content) }
else -> { content -> NiaBackground(content = content) }
}
代碼B
@Composable
fun NiaApp(
windowSizeClass: WindowSizeClass,
appState: NiaAppState = rememberNiaAppState(windowSizeClass)
) {
NiaTheme {
background(appState){
Scaffold(
...
) { padding ->
}
}
}
}
...
代碼 C
@Composable
fun NiaApp(
windowSizeClass: WindowSizeClass,
appState: NiaAppState = rememberNiaAppState(windowSizeClass)
) {
val aa=background(appState)
NiaTheme {
aa{
Scaffold(
...
) { padding ->
}
}
}
}
...
這與其說是一個Compose
問題,不如說是一個Kotlin
function
問題,因此我們將在這里省略所有與Jetpack Compose
相關的代碼,只留下與Kotlin
相關的上下文以使一切都成為焦點。
讓我們首先定義key points
並用字符標記它們。
Key point A
Lambda 調用:lambda .invoke
等於()
Key point B
. 基於文檔傳遞尾隨 lambda:
根據 Kotlin 約定,如果 function 的最后一個參數是 function,那么作為相應參數傳遞的 lambda 表達式可以放在括號外:
...
如果 lambda 是該調用中的唯一參數,則可以完全省略括號
……
我們將使背景 function 在沒有@Composable
注釋和NiaAppState
arg 的情況下看起來像這樣,不帶任何參數,但我們將保持 function 調用相同,以便我們走上正軌。 我還命名了返回的 lambda 參數以獲得更清晰的圖片。
fun background() : (anotherLambda: () -> Unit) -> Unit { ... }
代碼A用background(appState)(){...}調用時可以正常運行,為什么我不能去掉括號()?
但是代碼 B 在使用 background(appState) {...} 調用時失敗了,為什么?
讓我們同時分解您的CodeA
和CodeB
來回答您上面的 2 個問題。 但請記住,我們使用的是我們自己的background
function 而不是 compose 背景。
1:
先從一個簡單的調用開始,這里我們只是調用后台function,忽略它返回的值,沒什么異常
background()
2:
在這里,我們調用后台 function,同時調用返回的lambda (
Key point A
),這里會出現編譯錯誤 ' No value passed for parameter anotherLambda ' 因為當我們調用它時,它需要我們通過一個它的參數是() -> Unit
的一種類型
background()() // <-- compile error: No value passed for parameter anotherLambda
3:代碼 A 在使用 background(appState)() {...} 調用時可以很好地工作
當我們指定 lambda 塊 {...} 時,這里編譯錯誤消失了,因為
- 我們立即調用了返回的 lambda(
Key point A
)- 我們為它提供了一個 lambda 參數,並且由於代碼遵循
Key point B
,它通過簡單地調用參數 lambda 塊 {... }
background()() {
...
}
4:但是代碼 B 在使用 background(appState) {...} 調用時失敗了,為什么? 為什么我不能刪除括號 ()?
在這里,我們會得到另一種錯誤,“ Too many arguments for public fun background()... ”。
因為我們沒有調用返回的 lambda,所以我們只是調用 background() function 本身,它沒有任何 lambda 參數或根本沒有任何參數,請檢查我們在上面所做的背景 function 簽名和
Key point B
。而你的實際后台 function 只有一個參數 (appState: NiaAppState) 而不是 lambda 類型的參數,再次檢查
Key point B
。
background() { // big chunk of compile error here: Too many arguments for public fun background() ...
...
}
5:
這是沒有
Key point B
的版本(檢查#3)。 我們立即調用返回的 lambda(Key point A
)並在其中傳遞一個lambda參數。
background () ( {
...
} )
使用 lambda 的invoke()
而不是括號()
的等效background()
調用:
// #2
background().invoke() // <-- compile error: No value passed for parameter anotherLambda
// #3
background().invoke {
...
}
// #4
background(invoke { // big chunk of compile error here: Too many arguments for public fun background() ... and Unresolved reference invoke
...
} )
// #5
background().invoke ( {
...
} )
還有更多代碼 C 在用 val aa=background(appState) aa{...} 調用時可以正常工作,為什么?
最后讓我們分解CodeC
:
1:
這里我們調用后台 function,因為我們有一個類型推斷的賦值操作,
aa
現在是從 background() 調用返回的 lambda 值
val aa = background()
// your actual background function equivalent
val aa = background(appState)
2:
指定類型的賦值聲明。
// aa assignment declaration with type specified
val aa : (() -> Unit) -> Unit = background()
// your actual background function equivalent
val aa : @Composable (@Composable () -> Unit) -> Unit = background(appState)
3:
具有指定類型和 aa 的 lambda 參數定義名稱的賦值聲明。
// aa assignment declaration with type specified and named parameter
val aa : (aaLambdaParam : () -> Unit) -> Unit = background()
// your actual background function equivalent
val aa : @Composable (aaLambdaParam: @Composable () -> Unit) -> Unit = background(appState)
4:
aa
是返回的 lambda ,它接受() -> Unit
類型的參數,由於Key point B
,我們可以省略括號並直接調用傳遞的塊 {...} lambda 參數
aa {
...
}
5:
但是如果我們這樣調用它,我們會得到一個錯誤,'No value passed for parameter...',因為
aa
現在是一個 function ,它需要一個() -> Unit
類型的參數,參見Key point A
。
aa() // <-- No value passed for parameter 'p1' (#2) or 'No value passed for parameter 'aaLambdaParam' (#3)
6:
這是沒有
Key point B
的版本(檢查#4)。 我們調用 lambda(Key point A
)並在其中傳遞一個 lambda 參數。
aa ( {
...
} )
使用 lambda 的invoke()
而不是括號()
的等效aa
調用:
// #4, Key point B
aa.invoke {
...
}
// #5
aa.invoke() // <-- No value passed for parameter 'p1' or 'aaLambdaParam'
// #6, not Key point B
aa.invoke( {
...
} )
我建議重新訪問Kotlin 的高階函數和 Lambdas 。
這是一道高階題function,這個例子會更容易理解
fun getResult(): (innerLambda: () -> Int) -> Unit = { innerLambda ->
println(innerLambda())
println("result")
}
innerLambda 將返回一個數字,所以當我們調用 function getResult 時
getResult() // this is type (innerLambda: () -> Int) -> Unit
第二個括號是invoke
function,所以
getResult().invoke()
getResult()()
這兩行是一樣的,當我們到達這里時,我們必須將innerLambda
,所以它看起來像
getResult()({ 100 })
此時ide會提示你Lambda argument should be moved out of parentheses
,像這樣
getResult()(){ 100 }
結果版本
而對於版本C就簡單了,在函數式編程中用一個變量柯里化一個function很常見,調用柯里化function,所以當我們弄清楚版本A時,我們可以查看下面的代碼
val getApiResult:(innerLambda: () -> Int) -> Unit = getResult()
getApiResult.invoke({ 100 })
與之前示例相同的步驟,將 lambda 移出
getApiResult.invoke(){ 100 }
當 lambda 是最后一個參數時,我們可以刪除不必要的括號
getApiResult.invoke { 100 }
getApiResult() { 100 }
這兩行是相同的,記住在 Kotlin 中,當 lambda 是最后一個參數時,我們可以刪除不必要的括號,再次,最終我們得到了這個
getApiResult { 100 }
添加一些與上述答案相關的示例
class InnerLambda:Function0<Int> {
override operator fun invoke(): Int {
return 100
}
}
fun clickListener(event:() -> Unit) {
}
clickListener {
}
運算符 fun 是常用的,比如+
是unaryPlus
,它們是 Kotlin 語法糖,所以3.unaryPlus(5)
等於 Kotlin 中的3 + 5
,這就是為什么invoke
可以替換為()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.