考虑这个表达式:
someElement
{
// Some code
}
这是如何解释的?它是一个表达式(例如一个值,或者一个没有参数的函数调用),后跟一个用大括号括起来的块语句吗?还是用大括号括住单个参数的函数调用?
如果 Scala 没有分号推断——也就是说,如果 Scala 需要分号来表示语句的结尾,其方式与 Java 确实——那么这两者可以很容易地区分,因为前者需要在第一行的末尾使用分号。然而,Scala 解析器必须推断分号需要在哪里才能理解代码,有时它会出错。 (根据上下文,这两种解释都是有效的,Scala 解析器并不总是可以自行解决歧义。)
例如,假设someElement 是一个带有按名称 参数的函数。如果您尝试在 Scala REPL 中调用它并打算将参数(在大括号内)放在另一行,您会发现单独输入 someElement 会导致错误:
> scala
Welcome to Scala 2.12.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161).
Type in expressions for evaluation. Or try :help.
scala> def someElement(x: => Int): Int = {
| // Do something...
| x
| }
someElement: (x: => Int)Int
scala> someElement
<console>:13: error: missing argument list for method someElement
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `someElement _` or `someElement(_)` instead of `someElement`.
someElement
^
也就是说,您甚至没有进入大括号。但是,如果您输入以下内容,那么您就可以了:
scala> someElement {
| 10
| }
res0: Int = 10
但是如果someElement 是一个值呢?现在我们在 REPL 中看到了这一点:
scala> val someElement = 5
someElement: Int = 5
scala> someElement
res1: Int = 5
scala> {
| 5
| }
res2: Int = 5
现在 REPL 接受相同的代码,在不同的行中,作为两个不同的表达式。
让我们变得模棱两可。假设someElement 是一个值,但它现在是对采用单个参数的函数的引用。让我们看看可能的解释:
scala> def square(a: Int) = a * a
square: (a: Int)Int
scala> val someElement = square _
someElement: Int => Int = $$Lambda$1034/1609754699@74abbb
scala> someElement
res3: Int => Int = $$Lambda$1034/1609754699@74abbb
scala> {
| 5
| }
res4: Int = 5
也就是说,它被视为两个单独的语句:一个值后跟一个块语句。然而:
scala> someElement {
| 5
| }
res5: Int = 25
被视为对 square 的调用,参数为 5。
Scala 编译器比 REPL 更聪明一点,因为它可以一次查看所有代码,并且会尝试通过查看哪些替代方案来解决歧义最有意义,但它的解释可能并不总是与你的一致。
因此,如您所见,将左大括号放在同一行(如果两个表达式已链接)会使关系明确并消除歧义。或者,如果您希望将表达式明确解析为单独的语句,请在第一行之后添加分号。
(恕我直言,分号推断是 Scala 的 致命弱点之一。)