• Go语言函数类型

    在对Go语言的类型系统做了全面的讲解后,本节将对函数类型进行全面深入的介绍,首先介绍“有名函数”和“匿名函数”两个概念,使用 func FunctionName() 语法格式定义的函数我们称为“有名函数”,这里所谓的有名是指函数在定义时指定了“函数名”,与之对应的是“匿名函数”,所谓的匿名函数就是在定义时使用 func() 语法格式,没有指定函数名,通常我们所说的函数指的是“有名函数”。

    函数类型也分两种,一种是函数字面量类型(未命名类型),另一种是函数命名类型。

    函数字面量类型

    函数字面量类型的语法表达格式是 func(InputTypeList)OutputTypeList,“有名函数”和“匿名函数”的类型都属于函数字面量类型,有名函数的定义相当于初始化一个函数字面量类型后将其赋值给一个函数名变量,“匿名函数”的定义也是直接初始化一个函数字面量类型,只是没有绑定到一个具体函数名变量上。从 Go 类型系统的角度来看,“有名函数”和“匿名函数”都是函数字面量类型的实例。

    函数命名类型

    从前面章节知道可以使用 type NewType OldType 语法定义一种新类型,这种类型都是命名类型,同理可以使用该方法定义一种新类型——函数命名类型,简称函数类型,例如:

    type NewFuncType FuncLiteral

    依据Go语言类型系统的概念,NewFuncType 为新定义的函数命名类型,FuncLiteral 为函数字面量类型,FuncLiteral 为函数类型 NewFuneType 的底层类型,当然也可以使用 type 在一个函数类型中再定义一个新的函数类型,这种用法在语法上是允许的,但很少这么使用,例如:

    type NewFuncType OldFuncType

    函数签名

    有了上面的基础,函数签名就比较好理解了,所谓“函数签名”就是“有名函数”或“匿名函数”的字面量类型,所以有名函数和匿名函数的函数签名可以相同,函数签名是函数的“字面量类型”,不包括函数名。

    函数声明

    Go语言没有C语言中函数声明的语义,准确地说,Go 代码调用 Go 编写的函数不需要声明,可以直接调用,但 Go 调用汇编语言编写的函数还是要使用函数声明语句,示例如下。

    //函数声明=函数名+函数签名
    //函数签名
    func (InputTypeList)OutputTypeList
    //函数声明
    func FuncName (InputTypeList)OutputTypeList

    下面通过一个具体的示例来说明上述概念。

    //有名函数定义,函数名是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 类型的变量 g
    var g ADD = add
    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
    }

    前面谈到字面量类型是一种未命名类型 (unnamed type),其不能定义自己的方法,所以必须显式地使用 type 声明一个有名函数类型,然后为其添加方法,通常说的函数类型就是指有名函数类型,“函数签名”是指函数的字面量类型,在很多地方把函数类型和函数签名等价使用,这是不严谨的。

    由类型转换的规则可知,这两种类型的底层类型相同,并且其中一个是字面量类型,二者是可以相互转换的,下面来看一下经典的 http 标准库对函数类型的实现,进一步理解这种用法。

    //src/net/http/server.go
    //定义一个有名函数类型 HandlerFune
    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) HandleFune (pattern string, handler func (ResponseWriter,
    *Request)) {
        mux.Handle(pattern, HandlerFunc(handler))
    }

    通过 http 标准库里面对于函数类型的使用,我们可以看到函数类型的如下意义:

    • 函数也是一种类型,可以在函数字面量类型的基础上定义一种命名函数类型。
    • 有名函数和匿名函数的函数签名与命名函数类型的底层类型相同,它们之间可以进行类型转换。
    • 可以为有名函数类型添加方法,这种为一个函数类型添加方法的技法非常有价值,可以方便地为一个函数增加“拦截”或“过滤”等额外功能,这提供了一种装饰设计模式。
    • 为有名函数类型添加方法,使其与接口打通关系,使用接口的地方可以传递函数类型的变量,这为函数到接口的转换开启了大门。

更多...

加载中...