什么是闭包
闭包 就是一个函数和与其相关的引用环境组成的一个整体
直白点说,闭包就是一个函数用了它作用域之外的变量( 单纯理解的话理解到这里就够了,至于到底怎么做到用作用域之外的变量,就是语法的问题了 ),就像下面的例子
//闭包基础例子
object Closures {
var n: Int=10
def add10(i: Int): Int= {
//讲道理这个{}括起来的代码块才是add10的作用域
//这个n引用的就不是add10这个函数自己作用域内的变量
i+n
}
def main(args: Array[String]): Unit = {
println(add10(5))//15
}
}
还有两种对闭包的形容我觉得十分贴切:
1. 闭包就是一个有记忆的函数
2. 闭包相当于一个只有一个方法的紧凑对象
为什么这么说?
就像上面那个例子,函数add10似乎记住了变量n的值一样。当然你可能觉得牵强,那么我们换个方式实现一下闭包。
需求:写一个计数器
需求很简单,对于我们提笔就干。
object Closures {
var count=0
def counter():Int={
count+=1
count
}
def main(args: Array[String]): Unit = {
println(counter())//1
println(counter())//2
println(counter())//3
}
}
脑子都不用动就能想出上面这样的代码,能很好的解决这个需求。但是你可能会想到,这和最开始那个例子有什么不同吗?而且缺点很明显啊,中间随便一个另外的函数都能破坏这个计数器的计数功能。
object Closures {
var count=0
def counter():Int={
count+=1
count
}
def trubleMaker():Int={
count+=1
count
}
def main(args: Array[String]): Unit = {
println(counter())//1
println(counter())//2
trubleMaker() //如果中间发生任何意外,计数器功能就失败了
println(counter())//4
}
}
动动我们聪明的小脑瓜,想着把这个count变量放在counter()里面不就可以了吗?变成它自己的变量,只有它自己找得到。于是有了下面这样的代码
object Closures {
def counter():Int={
var count=0
count+=1
count
}
def main(args: Array[String]): Unit = {
println(counter())//1
println(counter())//1
println(counter())//1
}
}
不仅功能达不到需求了,而且闭包也不见了?不急,再改一下。
object Closures {
def Counter():()=>Int ={
var cnt: Int=0
def count(): Int ={
cnt=cnt+1
return cnt
}
return count
}
def main(args: Array[String]): Unit = {
var c=Counter()
println(c())//1
println(c())//2
println(c())//3
}
}
眉头一皱,发现事情并不简单。不急我们慢慢来看
首先,Counter返回的不再是一个Int而是一个函数了
再看,返回的函数count,每次拿Counter的cnt变量,然后+1再返回
最后把这个count函数返回
mian函数中用的时候,先拿到count()这个函数,调用count()达到了计数器的效果
这里整个Counter函数内部构成了一个闭包,记住了cnt这个变量的值。
至于为什么说“闭包相当于一个只有一个方法的紧凑对象”这个形容得也很贴切呢。看看下面这个例子
object Closures {
def Counter():()=>Int ={
var cnt: Int=0 //对象的属性 可以有多个
def count(): Int ={ //对象唯一的方法
cnt=cnt+1
return cnt
}
return count
}
def main(args: Array[String]): Unit = {
var c=Counter()
println("c--->"+c())//1
println("c--->"+c())//2
println("c--->"+c())//3
var d=Counter()
println("d--->"+d())//1
println("d--->"+d())//2
println("d--->"+d())//3
println("c--->"+c())//4
println("c--->"+c())//5
println("c--->"+c())//5
}
}
有什么好处
函数有了记忆功能(闭包),就产生了一些美妙的东西。这就是闭包的好处,下面体会一下。
需求:
我们现在有一堆文件
比如a.avi b c.avi d
有的有后缀,有的没有。我们需要给没有后缀名的文件加上后缀
一样,脑瓜子不用动,我们写出了下面的代码
object Closures {
//普通写法
def makeSuffix(fileName: String, suffix: String): String = {
if (!fileName.endsWith(suffix)) {
return fileName.concat(suffix)
}
fileName
}
def main(args: Array[String]): Unit = {
println(makeSuffix("xiaocang",".avi"))//xiaocang.avi
println(makeSuffix("xiaoji.avi",".avi"))//xiaoji.avi
}
}
这样其实 已经够了,但是不够完美,我们每次调用都要输入一次后缀,用了闭包,我们只需要一开始确定后缀,让函数记住它,更加方便。
object Closures {
//闭包写法
def makeSuffixClos(suffix: String): (String) => String = {
def makeSuffix(fileName: String): String = {
if (!fileName.endsWith(suffix)) {
return fileName.concat(suffix)
}
fileName
}
return makeSuffix
}
def main(args: Array[String]): Unit = {
val f = makeSuffixClos(".avi")
println(f("xiaocang"))//xiaocang.avi
println(f("xiaoji.avi"))//xiaoji.avi
}
}
再变一下(这里主要体现一下,其实柯里化也用到了闭包)
object Closures {
//柯里化写法
def makeSuffixCurrying(suffix: String)(fileName: String): String = {
if (!fileName.endsWith(suffix)) {
return fileName.concat(suffix)
}
fileName
}
def main(args: Array[String]): Unit = {
val f = makeSuffixCurrying(".avi") _ //这里转成函数一下
println(f("xiaocang")) //xiaocang.avi
println(f("xiaoji.avi")) //xiaoji.avi
}
}