如何调用一个返回 lambda 的函数,该函数也接受另一个 lambda 作为其参数 ( () -> Unit ) -> Kotlin 中的 Unit ?

Hel*_*oCW 7 lambda function-call higher-order-functions kotlin android-jetpack-compose

代码 A 来自Roman Y 回答的问题。

代码A调用with时可以正常工作background(appState)() {...},为什么不能去掉括号()?

但是代码 B 在调用 with 时失败了background(appState) {...},为什么?

而且更多的代码C在调用 with 时可以很好地工作 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) }
            } 
Run Code Online (Sandbox Code Playgroud)

代码B

@Composable
fun NiaApp(
        windowSizeClass: WindowSizeClass,
        appState: NiaAppState = rememberNiaAppState(windowSizeClass) 
) {
        NiaTheme {
            background(appState){
                Scaffold(
                    ...
                ) { padding ->
                  }
            }     
        }
}

...
Run Code Online (Sandbox Code Playgroud)

代码C

@Composable
fun NiaApp(
        windowSizeClass: WindowSizeClass,
        appState: NiaAppState = rememberNiaAppState(windowSizeClass) 
) {
        val aa=background(appState)

        NiaTheme {
            aa{
                Scaffold(
                    ...
                ) { padding ->
                  }
            }     
        }
}

...
Run Code Online (Sandbox Code Playgroud)

z.g*_*g.y 5

这与其说是一个Kotlin function问题Compose,不如说是一个问题,因此我们将Jetpack Compose在这里省略所有相关的代码,只留下Kotlin相关的上下文以保持所有内容的焦点。

\n

首先让我们key points用一个字符来定义和标记它们。

\n
    \n
  • Key point A。Lambda 调用:lambda.invoke等于()
  • \n
  • Key point B。基于文档
  • \n
\n
\n

传递尾随 lambda:

\n

根据 Kotlin 约定,如果函数的最后一个参数是函数,则可以将作为相应参数传递的 lambda 表达式放在括号之外:

\n

...

\n

如果 lambda 是该调用中的唯一参数,则可以完全省略括号

\n

\xe2\x80\xa6

\n
\n
\n

我们将使后台函数看起来像这样,没有@Composable注释和NiaAppState参数,没有参数,但我们将保持函数调用相同,这样我们就可以步入正轨。为了更清晰的描述,我还命名了返回的 lambda 参数。

\n
fun background() : (anotherLambda: () -> Unit) -> Unit { ... }\n
Run Code Online (Sandbox Code Playgroud)\n
\n

代码A在使用background(appState)()\n{...}调用时可以很好地工作,为什么我不能删除括号()?

\n

但是代码 B 在使用 background(appState) {...} 调用时失败,为什么?

\n
\n
\n

让我们分解您的问题CodeACodeB同时回答您上面的两个问题。但请记住,我们使用的是我们自己的 background函数,而不是 compose 函数。

\n
\n

1:

\n

首先从一个简单的调用开始,这里我们只是调用后台函数,并忽略\n它返回的值,这里没有什么异常

\n
\n
background()\n
Run Code Online (Sandbox Code Playgroud)\n
\n

2:

\n

在这里,我们在调用后台函数的同时,也立即INVOKING\n返回的lambda( )Key point A,\n我们会在这里得到一个编译错误\' No value pass forparameter anotherLambda \'\n因为当我们INVOKE它时,它要求我们传递一个参数给它,它是一种类型() -> Unit

\n
\n
background()() // <-- compile error: No value passed for parameter anotherLambda\n
Run Code Online (Sandbox Code Playgroud)\n
\n

3:代码A在使用background(appState)() {...}调用时可以很好地工作

\n

这里,当我们指定 lambda block\n{ ... } 时,编译错误消失了,因为

\n
    \n
  • 我们立即调用返回的 lambda( Key point A)
  • \n
  • 我们为它提供了一个 lambda 参数,并且由于代码遵循Key point B 它,因此只需调用参数 lambda\'s block { ... }
  • \n
\n
\n
background()() {\n   ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

4:但是代码 B 在使用 background(appState) {...} 调用时失败,为什么?为什么我不能删除括号 ()?

\n

在这里,我们会得到另一种错误,“ Too much arguments for public funbackground() ... ”。

\n

因为我们没有调用返回的 lambda,所以我们只是调用 background() 函数本身,并且它没有任何 lambda 参数或根本没有任何参数,请检查我们上面所做的后台函数签名和Key point B

\n

并且您的实际后台函数只有一个参数(appState:NiaAppState),并且它不是 lambda 类型参数,请再次检查Key point B

\n
\n
background() {  // big chunk of compile error here: Too many arguments for public fun background() ...\n   ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

5:

\n

这是不带 Key point B. 我们立即调用返回的 lambda ( Key point A)并其中 传递 lambda 参数。

\n
\n
background () ( {\n   ...\n} )\n
Run Code Online (Sandbox Code Playgroud)\n

background()使用 lambda 的invoke()\而不是括号进行等效调用()

\n
// #2\nbackground().invoke() // <-- compile error: No value passed for parameter anotherLambda\n\n// #3\nbackground().invoke {\n   ...\n}\n\n// #4\nbackground(invoke { // big chunk of compile error here: Too many arguments for public fun background() ... and Unresolved reference invoke\n   ...\n} )\n\n// #5\nbackground().invoke ( {\n    ...\n} )\n
Run Code Online (Sandbox Code Playgroud)\n
\n
\n

而且更多的代码C在使用val\naa=background(appState) aa{...}调用时可以很好地工作,为什么呢?

\n
\n

最后让我们分解一下CodeC

\n
\n

1:

\n

这里我们调用后台函数,因为我们有一个类型推断的赋值操作,aa现在是从background()\nin Vocation 返回的 lambda 值

\n
\n
val aa = background()\n\n// your actual background function equivalent\nval aa = background(appState)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

2:

\n

指定类型的赋值声明。

\n
\n
// aa assignment declaration with type specified\nval aa : (() -> Unit) -> Unit = background()\n\n// your actual background function equivalent\nval aa : @Composable (@Composable () -> Unit) -> Unit  = background(appState)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

3:

\n

具有指定类型和 aa 的 lambda 参数的定义名称的赋值声明。

\n
\n
// aa assignment declaration with type specified and named parameter\nval aa : (aaLambdaParam : () -> Unit) -> Unit = background()\n\n// your actual background function equivalent\nval aa : @Composable (aaLambdaParam: @Composable () -> Unit) -> Unit  = background(appState)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

4:

\n

aa是返回的 lambda,它接受类型的参数() -> Unit,因为Key point B,我们可以省略括号并直接调用传递 lambda 参数的块 {...}

\n
\n
aa {\n   ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n
\n

5:

\n

但是如果我们这样调用它,我们会得到一个错误,“没有为参数传递值...”,因为aa现在是一个需要类型为参数的函数() -> Unit,请参阅Key point A

\n
\n
aa() // <-- No value passed for parameter \'p1\' (#2) or \'No value passed for parameter \'aaLambdaParam\' (#3)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

6:

\n

这是不带 Key point B. 我们调用 lambda ( Key point A) 并在其中传递一个 lambda 参数。

\n
\n
aa ( {\n   ...\n} )\n
Run Code Online (Sandbox Code Playgroud)\n

aa使用 lambda 的invoke()\而不是括号进行等效调用()

\n
// #4, Key point B\naa.invoke {\n   ...\n}\n\n// #5\naa.invoke() // <-- No value passed for parameter \'p1\' or \'aaLambdaParam\'\n\n// #6, not Key point B\naa.invoke( {\n   ...\n} )\n
Run Code Online (Sandbox Code Playgroud)\n

我建议重新访问Kotlin 的高阶函数和 Lambda

\n