Scala - 方法调用语法

12

我是Scala的初学者,看到了几种不同的调用方法的语法。有些很好,比如对于没有参数的方法可以忽略括号,或者像下面这样省略掉点号:

1 to 10

但有一些真的让我感到困惑,例如:

breakable { ... }

这只是一个方法调用,对吗?我还能为多个参数或不是无参函数的参数这样做吗?

谢谢


2
是的,这是一个闭包。您正在将代码块传递到方法中。请注意,在Scala中,您可以使用{}替代()来传递单个参数给方法。 - aishwarya
你在这里有什么困扰吗?是方法名后面的代码块({...})部分还是我们没有在方法前缀中加入对象/类名的事实? - Nicolas
2个回答

26

调用方法有两种标准方式:

obj.method(params)   // dot notation
obj method (params)  // operator notation
上述内容可以通过以下方式进行修改:
  1. 如果params是单个参数,则可以使用{}替换()
  2. 如果params是单个参数,并且您正在使用运算符符号,那么可以省略括号。
  3. 如果method不需要参数,则可以省略(params)(即删除空的())。
  4. 如果method:结尾,则实际上将绑定到右边的操作符符号。也就是说,(params) method_: obj等同于obj.method_:(params)
  5. 无论哪种方式,只要可以区分标识符,空格都是可选的。所以可以向点符号添加空格,例如obj . method ( params )或将.method(params)写在下一行,就像调用链接一样,还可以从操作符符号中移除空格,例如a+b

还有一些关于元组推断的东西,但我尽量避免使用它,所以对准确的规则不太确定。

然而,这些都无法解释您困惑的示例。在我解释之前,我想展示一些语法糖,也可以用于调用方法:

obj(params) // equivalent to obj.apply(params)
obj.x = y   // equivalent to obj.x_=(y), if obj.x also exists
obj(x) = y  // equivalent to obj.update(x, y)
obj op= y   // equivalent to obj = obj op y, if op is symbolic
~obj        // equivalent to obj.unary_~; also for !, + and -, but no other symbol

好的,现在让我们来看看你提供的示例。人们可以通过稳定值进行成员导入。Java可以使用其静态导入来导入静态方法,但Scala有一种更通用的机制:从包、对象或常见实例中导入没有区别:它会引入类型成员和值成员。方法属于后者。

因此,假设您有一个val a = 2,并且您执行import a._。这将使所有Int方法进入范围,因此您可以直接调用它们。您不能执行+(2),因为那将被解释为对unary_+的调用,但您可以调用*(4),例如:

scala> val a = 2
a: Int = 2

scala> import a._
import a._

scala> *(4)
res16: Int = 8

现在,这是规则。你可以调用

method(params)

如果:

  1. method已经被导入到作用域中。
  2. 即使只有一个参数,也要保留括号。

请注意,还存在优先级问题。如果您编写 obj method(params),Scala将假定 method 属于 obj,即使它已被导入到作用域中。


很好的回答。我只是对你的第一点有一个问题,关于能够使用{ }符号而不是()来调用方法。这种语法为什么会被支持?这种语法似乎在许多框架及其文档示例中都被广泛使用。我想知道这种特定语法是否有非常具体的功能原因。当将闭包作为参数传递给函数时,它通常被使用吗?我想了解它的根源,以便我可以清晰地理解和推理。谢谢。 - HiChews123
2
@acspd7 好的,(){}在表达式内有不同的含义。也就是说,2 * (3 + 4)2 * {3 + 4}的结果相同,但它们是不同的东西。你只能在括号内放置表达式,而将定义和语句放在花括号内,在其中最后一个语句的值是表达式的值(花括号本身是一个表达式)。f(...)可以替换为 f{...} 的原因只是为了让人们不必写 f({...})。这使人们能够编写看起来本地化的“用户”控制结构。 - Daniel C. Sobral

15

如果我们进行脱糖处理,那么我们将得到:

breakable({ ... }) 

这个匹配了签名

breakable: (op:  Unit): Unit 

并且使用所谓的按名称调用参数(你可以将其看作将一块代码作为参数传递

此外,scala还允许您编写以下内容:

scala> def foo (op1: => Unit)(op2: => Unit) = {op1;op2;}
foo: (op1: => Unit)(op2: => Unit)Unit

scala> foo { println(1) } { println(2) }
1
2
上面是柯里化函数的示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接