我可以在Scala的Predef类中使用String作为第二个参数,例如,
require ("foo" == "bar", "foobar")
首先考虑将require方法作为第二个参数重载为不同的参数。但事实并非如此。 require方法的签名(Scala 2.9.1)是:
require(requirement: Boolean, message: ⇒ Any): Unit
为什么上述方法调用可能?
答案 0 :(得分:21)
我不完全理解这个问题,但这里有一点解释。 require
在Predef
中有一个重载版本:
def require(requirement: Boolean) //...
def require(requirement: Boolean, message: => Any) //...
由于message: => Any
类型,第二个有点混乱。如果简单的话可能会更容易:
def require(requirement: Boolean, message: Any) //...
第二个参数当然是一条消息,假设如果不满足断言则将其附加到错误消息中。您可以想象message
应为String
类型,但Any
只能写:
require(x == 4, x)
如果x
不等于Int
,则会将4
(类型为Any
)的实际值添加到错误消息中。这就是选择: =>
的原因 - 允许任意值。
但require(list.isEmpty, list.size)
部分怎么样?这称为按名称调用,基本上意味着:在访问时评估此参数。想象一下以下片段:
list
在这种情况下,您需要确保list
为空 - 如果不是,请将实际list.size
大小添加到错误消息中。但是,对于正常的调用约定,{<1}}部分必须在调用方法之前进行评估 - 这可能是浪费的。使用按名称调用约定,list.size
仅在第一次使用时进行评估 - 当错误消息是构造函数(如果需要)时。
答案 1 :(得分:3)
require
方法在scala具有默认参数(在2.8中引入)之前存在于Predef
中,因此如果您想要给定参数的默认行为,则重载是唯一的选项。如消息所示,第二个参数可以是任何内容,然后将其用作抛出message
的{{1}}(通过调用其toString
方法),( if < / em>它被抛出 - 即如果要求失败)。
请注意,参数实际上是按名称调用;也就是说,它被声明为IllegalArgumentException
,这意味着只有在需求失败时才会被评估。
这会以对象创建的形式造成性能损失,但在消息构造昂贵的情况下(可能需要对数据结构进行一些O(n)访问)可能会有用。
答案 2 :(得分:2)
问题是,为什么String是第二个参数的有效类型 虽然签名说它必须是一个函数
message: => Any
。
签名没有说明。签名表示第二个参数是类型Any
,并且此参数是按名称传递。
“by name”由前缀=>
符号表示,该符号 not mean函数 - 函数始终表示为输入参数 =&gt ; 结果类型,Function0
为() => type
。
按“值”和“按名称”查找参数。
答案 3 :(得分:-1)
答案很简单:你期待
的第二个参数require(boolean: Boolean, message: => Any): Unit
成为任何的功能并询问为什么String
有效,即使它不是功能。< / p>
将其细分为:固定的String
只不过是功能
()
个括号实际上,以下两个语句在Scala中是等效的:
def x: String = "ABC" // const value, even though 'def' implies a function
val x: String = "ABC"
因此,您可能会说=> Any
符合String
。