Scala学习:传名参数by-name parameter
创始人
2024-04-19 01:51:01
0

上节展示的withPrintWriter方法不同于语言的内建控制结构,如if和while,在于大括号之间的代码带了参数。withPrintWriter方法需要一个类型为PrintWriter的参数。这个参数以“writer =>”方式显示出来:

  1. withPrintWriter(file) {  
  2.  writer => writer.println(new java.util.Date)  
  3. }  

然而如果你想要实现某些更像if或while的东西,根本没有值要传入大括号之间的代码,那该怎么做呢?为了解决这种情况,Scala提供了传名参数。

51CTO编辑推荐:Scala编程语言专题

为了举一个有现实意义的例子,请设想你需要实现一个称为myAssert的断言架构。你只能称其为myAssert,而不是assert,因为Scala提供了它自己的assert,将在14.1节描述。myAssert函数将带一个函数值做输入并参考一个标志位来决定该做什么。如果标志位被设置了,myAssert将调用传入的函数并证实其返回true。如果标志位被关闭了,myAssert将安静地什么都不做。

如果没有传名参数,你可以这样写myAssert:

  1. var assertionsEnabled = true 
  2. def myAssert(predicate: () => Boolean) =  
  3.  if (assertionsEnabled && !predicate())  
  4.   throw new AssertionError  
这个定义是正确的,但使用它会有点儿难看:

  1. myAssert(() => 5 > 3)  
你或许很想省略函数文本里的空参数列表和=>符号,写成如下形式:

  1. myAssert(5 > 3) // 不会有效,因为缺少() => 
传名函数恰好为了实现你的愿望而出现。要实现一个传名函数,要定义参数的类型开始于=>而不是() =>。例如,你可以通过改变其类型,“() => Boolean”,为“=> Boolean”,把myAssert的predicate参数改为传名参数。代码9.5展示了它的样子:

  1. def byNameAssert(predicate: => Boolean) =  
  2.  if (assertionsEnabled && !predicate)  
  3.   throw new AssertionError  
代码 9.5 使用传名参数

现在你可以在需要断言的属性里省略空的参数了。使用byNameAssert的结果看上去就好象使用了内建控制结构:

  1. byNameAssert(5 > 3)  
传名类型中,空的参数列表,(),被省略,它仅在参数中被允许。没有什么传名变量或传名字段这样的东西。

现在,你或许想知道为什么你不能简化myAssert的编写,使用陈旧的Boolean作为它参数的类型,如:

  1. def boolAssert(predicate: Boolean) =  
  2.  if (assertionsEnabled && !predicate)  
  3.   throw new AssertionError 
当然这种格式同样合法,并且使用这个版本boolAssert的代码看上去仍然与前面的一样:

  1. boolAssert(5 > 3)  
虽然如此,这两种方式之间存在一个非常重要的差别须指出。因为boolAssert的参数类型是Boolean,在boolAssert(5 > 3)里括号中的表达式先于boolAssert的调用被评估。表达式5 > 3产生true,被传给boolAssert。相对的,因为byNameAssert的predicate参数的类型是=> Boolean,byNameAssert(5 > 3)里括号中的表达式不是先于byNameAssert的调用被评估的。而是代之以先创建一个函数值,其apply方法将评估5 > 3,而这个函数值将被传递给byNameAssert。

因此这两种方式之间的差别,在于如果断言被禁用,你会看到boolAssert括号里的表达式的某些副作用,而byNameAssert却没有。例如,如果断言被禁用,boolAssert的例子里尝试对“x / 0 == 0”的断言将产生一个异常:

  1. scala> var assertionsEnabled = false 
  2. assertionsEnabled: Boolean = false 
  3. scala> boolAssert(x / 0 == 0)  
  4. java.lang.ArithmeticException: / by zero  
  5.  at .< init>(< console>:8)  
  6.  at .< clinit>(< console>)  
  7.  at RequestResult$.< init>(< console>:3)  
  8.  at RequestResult$.< clinit>(< console>)...  
但在byNameAssert的例子里尝试同样代码的断言将不产生异常:

  1. scala> byNameAssert(x / 0 == 0)  

【相关阅读】

  1. Scala:如何编写新的控制结构
  2. Scala学习:Curry化的函数
  3. Scala学习:简化客户代码
  4. 减少Scala中的代码重复
  5. Scala:尾递归的跟踪调用及其局限

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...