每个问题在本网站的其他地方都有答案,但我认为没有什么可以将它们全部处理。因此:
花括号和等号
使用等号定义的方法返回一个值(任何最后一件事情评估)。只使用花括号定义的方法返回Unit。如果您使用等号,但最后一件事评估为Unit,则没有区别。如果是等号后的单个语句,则不需要花括号;这对字节码没有影响。因此,1、2和5本质上是相同的:
def f1(s: String) = { println(s) }
def f2(s: String) { println(s) }
def f5(s: String) = println(s)
函数与方法
一个函数通常被写作 A => B
,是 Function
类的子类之一,例如 Function1[A,B]
。由于这个类有一个 apply
方法,在使用括号而没有方法名时,Scala 会自动调用它,看起来像是一个方法调用,实际上它是在那个 Function
对象上进行的调用!所以如果你写下:
def f3 = (s: String) => println(s)
那么你的意思是"
f3
应该创建一个
Function1[String,Unit]
实例,它具有一个类似于
def apply(s: String) = println(s)
的
apply
方法"。因此,如果你调用
f3("Hi")
,这首先会调用
f3
来创建函数对象,然后调用
apply
方法。
每次使用时都创建函数对象非常浪费资源,因此将函数对象存储在var中更为合理:
val f4 = (s: String) => println(s)
这个对象保存了一个相同函数对象的实例,它与
def
(方法)返回的一样,因此你不必每次重新创建它。
何时使用什么:
人们对于
: Unit = ...
和
{ }
的约定有所不同。个人而言,我所有返回
Unit
的方法都不带等号——这对我来说是一个指示,表明该方法几乎肯定没有用,除非它具有某种副作用(改变变量,执行IO等)。此外,我通常只在必要时使用大括号,因为有多个语句或单个语句过于复杂,我希望有一个视觉辅助工具告诉我它在哪里结束。
当你想要一个方法时,应该使用方法。每当你想将函数对象传递到其他方法中使用它们时(或每当你想能够应用函数时),都应创建函数对象(或应将其指定为参数)。例如,假设你想要能够缩放值:
class Scalable(d: Double) {
def scale() = ...
}
您可以提供一个常数乘数。或者您可以提供要添加和要乘的内容。但最灵活的是,您只需请求从 Double
到 Double
的任意函数:
def scale(f: Double => Double) = f(d)
现在,你可能对于“默认”的比例有了一个想法。那可能根本没有缩放。因此,您可能需要一个函数,它接受 Double
并返回完全相同的 Double
。
val unscaled = (d: Double) => d
我们将函数存储在一个val
中,因为我们不想一遍又一遍地创建它。现在我们可以将这个函数用作默认参数:
class Scalable(d: Double) {
val unscaled = (d: Double) => d
def scale(f: Double => Double = unscaled) = f(d)
}
现在我们可以调用
x.scale
、
x.scale(_*2)
以及
x.scale(math.sqrt)
,它们都可以正常工作。
Object
)。在Scala中,所有可以被程序操纵的内容(即每个object)也都是一个Object
,也就是一个类的实例(不像Java中,原始类型虽然可以被程序操纵(即属于“对象”),但它们不是Object
)。在Scala中,这种区别不存在:每个object都可以视为是一个Object
。 - Jörg W MittagObject
”,也就是说,程序可以操作的所有东西也是对象系统的成员,或者换句话说,在对象系统之外没有值(不像Java中的原始类型)。我们并不是指语言中存在的所有东西都可以在运行时由程序操纵。请注意,对于方法,这种区别有些模糊,因为Scala会自动将方法转换为函数。 - Jörg W Mittagforeach
期望一个函数,但你正在传递一个方法,但方法的签名与期望的函数相匹配,所以它会为你创建一个函数。 - Jörg W Mittag