scala是一种函数式编程风格的语言,除了常见的if......else ,for ,while等传统的流程控制结构,也可以自定义流程控制的控制结构。
再了解scala如何实现编写新的流程结构,我们了解一下头等函数的概念;
scala的函数是头等函数(first-class function).你不仅可以定义和调用函数。还可以把他们写成匿名的字面量(Iiteral),并把他们作为值(value)传递。
在拥有头等函数的语言中,即使语言的语法是固定的,你也可以有效地制作新的控制流程结构,可以使用创建带函数参数的方法来做到这点;另一种方法是
使用用花括号代替小括号的机制。
(一)使用创建带函数参数的方法来编写新的控制结构
下面是"双倍"控制结构的实现,能够重复一个操作两次并返回结果:
def twice(op: Double => Double,x: Double) = op(op(x)) #调用方法 twice(_ + 1,5)
twice函数解析:该函数有2两个参数,
第一个参数是function,作用是把一个double转化成另一个double。
第二个参数double类型的数字。
op(x)的意思是调用op对象的apply() 方法,由于op是函数值对象,所以op(x)的意思是,将x应用为op函数的参数。
所以 twice(_ + 1,5) 的返回值是 7.
当你在代码中发现,有个重复的控制模式时,就可以考虑把这些重复的控制模式实现成一个新的控制结构。
文件操作是的流程是:打开一个资源,对它进行操作,关闭资源。这是一个经常使用的代码模式。我们可以新建
一个控制结构来实现这个功能。
/** * withPrintWriter 方法有两个参数: * 第一个参数的值:file对象。 * 第一个参数的值:function类型的对象,其中function的参数是PrintWriter,返回值是Unit。 * */ def withPrintWriter(file: File,op: PrintWriter => Unit){ val writer = new PrintWriter(file) try{ op(writer) }finally{ writer.close() println("closed the print writer.") } }
//调用withPrintWriter方法
withPrintWriter(new File("D:/loader.log"), writer => {println("write a time str to file.");writer.print(new java.util.Date)})
withPrintWriter方法调用过程分析,
withPrintWriter(new File("D:/loader.log"), writer => {println("write a time str to file.");writer.print(new java.util.Date)})
第一个参数的值:file对象。
第一个参数的值:function类型的对象,该function的功能是将一个时间戳写入到这个文件中,并在写之前打印一条提示日志。
使用withPrintWriter的好处是:withPrintWriter并非客户代码,可以确保文件在结尾被关闭;
因此客户忘记关闭文件是不可能的。这个技巧被称为借贷模式(loan parttern)。
拿withPrintWriter来讲:withPrintWriter打开了资源并"贷出"给函数, withPrintWriter把printWriter借给
函数op;当函数op完成时,它发出信号说明它不再需要 "借"的资源PrintWriter,于是资源在finally块中被关闭,
以确信其确实被关闭,而忽略函数是正常结束返回还是抛出了异常。
(二)使用用花括号代替小括号的机制实现
为了让客户代码看上去更像内建控制结构的另一种方式是使用花括号代替小括号包围参数列表。
scala的任何方法调用,如果你确实只传入一个参数,就可以选择使用花括号代替小括号包围参数。
例如如下代码:
println("hello,world") 如果有一个参数,可以用花括号代替小括号包围参数。 println{"hello,world"} val g = "Hello,world!" g.substring(7,9) // g.substring{7,9} //如果有两个参数,就不能用花括号代替小括号包围参数,会报出编译错误
为了使用{}替换小括号,使用柯里化重新定义前一函数withPrintWriter,源码如下:
/** * 使用柯里化重新定义函数。 * scala在传入一个参数时,可以用花括号代替小括号的机制: * 这个机制的目的是让客户端程序能写出包围在花括号内的函数字面量;这可以让方法调用感觉更像控制抽象。 */ def withPrintWriter2(file: File)(op: PrintWriter => Unit) = { val writer = new PrintWriter(file) try{ op(writer) }finally{ writer.close() } } //调用该函数。 //调用柯里化的withPrintWriter2方法,第一函数参数以小括号包围参数,第二个一花括号包围参数(参数是函数字面量)。 //这种方法调用看起来是不是比较赏心悦目啊! withPrintWriter2(new File("D:/kafka_loader.log")){ writer => writer.print(new java.util.Date) } //当然也可以都用小括号,或者都用大括号,也是没有问题的?看个人喜好了,但是代码就有点怪异! withPrintWriter2(new File("D:/kafka_loader.log"))( writer => writer.print(new java.util.Date) ) withPrintWriter2{new File("D:/kafka_loader.log")}{ writer => writer.print(new java.util.Date) } // 简化一下调用方法 val file = new File("D:/kafka_loader.log") val func = (writer: PrintWriter) => writer.print(new java.util.Date) withPrintWriter2(file){func} //都是合法的哦,这种更像控制抽象。 withPrintWriter2(file)(func) //都是合法的哦 withPrintWriter2{file}{func} //都是合法的哦 withPrintWriter2{file}(func) //都是合法的哦