什么时候是block,什么时候是lambda?



Kotlin编译器如何决定{}中包含的表达式是块还是lambda?

考虑一下:

val a: Int
if (cond)
a = 1
else
a = 2

可以更简洁地写成:

val a =
if (cond)
1
else
2
类似地,有人可能会认为:

val a: () -> Int
if (cond)
a = { 1 }
else
a = { 2 }

应该可以写得更简洁,像这样:

val a =
if (cond)
{ 1 }
else
{ 2 }

但这是不一样的:a现在是Int,而不是() -> Int类型,因为现在{ 1 }不再是一个lambda。是什么规则决定一个是还是一个块?

我没有研究Kotlin词法分析器,但是我猜想在代码中很少有编译器期望单个表达式或代码块的地方。这包括紧跟在大多数控制流语句后面的代码,如:if,else,while,when(其中一种情况)等。如果您将{放在这些位置之一,它将被解释为与此控制流语句相关的代码块的开始,而不是作为lambda。

就是这么简单。请注意,即使您提示编译器类型,它仍然不会工作:

// compile error
val a: () -> Int = if (cond)
{ 1 }
else
{ 2 }

可以这样解释:

// compile error
val a: () -> Int = if (cond) {
1
} else {
2
}
if

条件之后的{总是被解释为代码块的开始。你需要在这样的情况下添加双{,}:

// works fine
val a: () -> Int = if (cond) {
{ 1 }
} else {
{ 2 }
}

简单地说,if/when/else/for后面的第一个左括号总是被认为是一个块的开始。如果你想要一个lambda,请使用双括号。

这在Kotlin语言规范第1.2节:语法语法:

中指定语法将定义为{}之间的语句。在大多数情况下(比如函数体),块和lambda的语法是不同的。在controlStructureBody的使用中是相同的-这些是块具有值的地方,或者您可以在其位置放置非块表达式。如果您在整个规范文档中搜索"controlStructureBody",您会发现它在以下地方使用:

为声明
  • <
  • 而声明/gh><
  • 延伸的声明/gh><
  • 如果表达式/gh><
  • 当表达式/gh><
  • 当条目/gh>

在其他需要值的地方,'{'表示lambda的开始。

最新更新