函数类型
使用func FunctionName()语法格式定义的函数我们称为“有名函数”,这里所谓的有名是指函数在定义时指定了“函数名”;与之对应的是“匿名函数”,所谓的匿名函数就是在定义时使用func()语法格式,没有指定函数名。通常所说的函
数就是指“有名函数”。
函数类型也分两种,一种是函数字面量类型(未命名类型),另一种是函数命名类型。
函数字面量类型
函数字面量类型的语法表达格式是func (InputTypeList) OutputTypeList
,可以看出“有名函数”和“匿名函数”的类型都属于函数字面量类型。有名函数的定义相当于初始化一
个函数字面量类型后将其赋值给一个函数名变量;“匿名函数”的定义也是直接初始化一个函数字面量类型,只是没有绑定到一个具体变量上。从Go类型系统的角度来看,“有名函数”和“匿名函数”都是函数字面量类型的实例。
函数命名类型
使用type NewType 0ldType语法定义一种新类型,这种类型都是命名类型,同理可以使用该方法定义一种新类型:函数命名类型,简称函数类型。例如:
``go
type NewFuncType FuncLiteral
依据Go语言类型系统的概念,NewFuncType为新定义的函数命名类型,FuncLiteral为函数字面量类型,FuncLiteral为函数类型NewFuncType的底层类型。当然也可以使用type在一个函数类型中再定义一个新的函数类型,这种用法在语法上是允许的,但很少这么使用。例如:
```go
type NewFuncType oldFuncType
函数声明
Go语言没有C语言中函数声明的语义,准确地说,Go代码调用G0编写的函数不需要声明,可以直接调用,但Go调用汇编语言编写的函数还是要使用函数声明语句,示例如下。
//函数声明=函数名+函数签名
//函数签名
func (InputTypeList) OutputTypeList
//函数声明
func FuncName (InputTypeList) OutputTypeList
下面通过一个具体的示例来说明上述概念。
package main
import "fmt"
func main() {
f := func(a, b int) int {
return a + b
}
g(1, 2)
f(1, 2)
//f和add的函数签名相同
fmt.Printf("%T\n", f)//func(int, int) int
fmt.Printf("%T\n", add)//func(int, int) int
}
//有名函数定义,函数名是add
//add类型是函数字面量类型 func (int, int) int
func add(a, b int) int {
return a + b
}
//函数声明语句,用于Go代码调用汇编代码
//func add(int, int) int
//add函数的签名,实际上就是add的字面量类型
//func (int, int) int
//匿名函数不能独立存在,常作为函数参数、返回值,或者赋值给某个变量
//匿名函数可以直接显式初始化
//匿名函数的类型也是函数字面量类型func (int, int) int
//func (a, b int) int {
// return a + b
//}
//新定义函数类型ADD
//ADD底层类型是函数字面量类型 func (int, int) int
type ADD func(int, int) int
//add和ADD的底层类型相同,并且add是字面量类型
//所以add可以直接赋值给ADD类型的变量
var g ADD = add
前面谈到字面量类型是一种未命名类型(unnamed type),其不能定义自己的方法,所以必须显式地使用tye声明一个有名函数类型,然后为其添加方法。通常说的函数类型就是指有名函数类型,“函数签名”是指函数的字面量类型,在很多地方把函数类型和函数签名等价使用,这是不严谨的。由类型转换的规则可知:这两种类型的底层类型相同,并且其中一个是字面量类型,二者是可以相互转换的。下面来看一下经典的http标准库对函数类型的实现,进一步理解这种用法。
//src/net/http/server.go
//定义一个有名函数类型HandlerFunc
type HandlerFunc func(ResponseWriter, *Request)
//为有名函数类型添加方法,属于一种装饰器方法
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
//函数类型HandlerFunc实现了接口Handler的方法
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
func (mux *ServeMux) Handle(pattern string, handler Handler)
//所以HandlerFunc类型的变量可以传递给Handler接口变量
func(mux *ServeMux) HandlerFunc(pattern string, handler func(ResponseWriter, *Request)){
mux.Handler(pattern, HandlerFunc(handler))
}
通过hp标准库里面对于函数类型的使用,我们可以看到函数类型的如下意义:
- 函数也是一种类型,可以在函数字面量类型的基础上定义一种命名函数类型。
- 有名函数和匿名函数的函数签名与命名函数类型的底层类型相同,它们之间可以进行类型转换。
- 可以为有名函数类型添加方法,这种为一个函数类型添加方法的技法非常有价值,可以方便地为一个函数增加“拦截”或“过滤”等额外功能,这提供了一种装饰设计模式。
- 为有名函数类型添加方法,使其与接口打通关系,使用接口的地方可以传递函数类型的变量,这为函数到接口的转换开启了大门。