Go语言既不像Java和C#那样是面向对象的编程语言,也不像Lisp和Haskell那样是纯函数式的编程语言。在Go语言中,函数是“一等公民”。
函数声明
func 函数名(参数列表) (返回值列表){ 函数体 }
函数的定义以func关键字开头,函数名与func关键字以空格分隔,函数名规范需符合Go语言中的标识符规范。函数可以有零个或多个参数,也可以有零个或多个返回值。
示例
func add(a int,b int) int { return a + b }
函数特点
多值返回
函数允许返回多个值,多个返回值时需要将返回类型列表用()括起来。
不定参数
函数支持不定数目的形式参数,不定参数声明使用...type格式。在函数体内部,不定参数是一个slice类型,所有对slice类型的操作同样也适用于不定参数。
func printList(arr ...int){ for _, value := range arr { fmt.Println(value) } }
Go函数对不定参数有一定的约束:
- 函数只能有一个不定参数。
- 不定参数必须是函数形参列表的最后一个。
- 不定参数作为另一个含有不定参数的函数的实参数传递时需要通过“...”来解参传递
不支持参数默认值
Go语言为了追求显式的表达,避免隐含,因此不支持参数默认值。
不支持函数重载
其它语言中,例如Java、C#都支持方法重载,即同一个类中有多个同名方法,但参数类型或参数数量不同。Go语言不支持同一个作用域范围内多个函数名称相同的情况,因此也不支持 函数重载。
匿名函数
上述的函数都是具名函数,与具名函数相对应的叫匿名函数。Go语言同样支持匿名函数,匿名函数没有函数名称,可以在其它函数体内定义。
匿名函数用得最频繁的应该是在defer延迟执行语句,示例如下:
func Inc() (value int){ defer func(){value++} return 9 }
匿名函数还可以做为函数的返回值,示例如下:
package main import "fmt" func main () { // 调用高阶函数add,完成3+2的计算 sum:=add(3)(2) fmt.Printf("3+2=%d\n",sum) } // add函数返回一个匿名函数 func add(a int) func(int) int{ return func(b int) int{ return a+b } }
匿名函数还可以赋值给一个变量,示例如下:
package main import "fmt" func main () { // 可以像调用普通具名函数那样调用函数变量 result:=subtract(3,2) fmt.Printf("3-2=%d\n",result) } // 声明一个函数类型变量,并用匿名函数初始化 var subtract = func(a,b int) int { return a-b }
函数类型
Go语言中,可以将函数作为变量的数据类型,也可以将函数作为参数的数据类型,还可以将函数作为函数返回值的数据类型。
定义一个函数类型:
type Subtracter func(a,b int ) int
定义函数类型的变量
func do() { var subtract Subtracter // 定义一个函数类型的变量subtract // 初始化subtract变量 subtract= func(a, b int) int { return a-b } // 调用subtract函数类型的变量 result:=subtract(3,2) fmt.Printf("3-2=%d\n",result) }
将函数作为参数类型
func do() { var subtract Subtracter subtract= func(a, b int) int { return a-b } // 将函数类型的变量传递为doSubtract函数 result:=doSubtract(subtract) fmt.Printf("3-2=%d\n",result) } // 将函数类型作为形参 func doSubtract(fun Subtracter) int { return fun(3,2) }
将函数作为返回值类型
func do() { subtract:=getSubtract() result:=doSubtract(subtract) fmt.Printf("3-2=%d\n",result) } // 将函数类型作为返回值 func getSubtract() Subtracter{ return func(a, b int) int { return 3-2 } }
函数闭包
函数体内声明的局部变量作用域仅限于该函数体内,函数体外部无法访问。另外,函数体内声明的局部变量生命周期会随函数的返回而结束(不考虑局部变量被作为引用返回的情形)。但是,通过闭包不仅可以让局部变量被函数体外部访问,还能延长局部变量的生命周期。
func main(){ w,r:=func1() w(13) // 修改value值 result:=r() // 读取value值 fmt.Printf("result:%d\n",result) } //声明两个函数类型writer,reader type ( writer func(int) reader func()int) //定义函数func1,返回writer,reader函数类型 func func1() (writer,reader){ var value int //局部变量的生命周期被延长,不再随func1调用结束而结束 w:=func(a int){ //返回一个可以修改value值的函数 value=a } r:=func()int{ //返回一个可以读取value值的函数 return value } return w,r }
闭包的底层实现
在编译器层面,Go语言编译器生成一个struct,将value变量以函数writer、reader指针作为struct的成员字段,并返回给func1的调用者。