第16章 函数类型

第16章 函数类型

“函数是 Go 的一等公民(first-class citizen)。” —— 这是 Go 语言的设计哲学之一。

在 Go 里,函数不只是用来调用的代码块——函数本身也是一种类型,可以像变量一样赋值、传递、作为参数、作为返回值。听起来有点抽象,但它带来的编程范式革命是巨大的:你可以在 Go 里写高阶函数、装饰器、回调、策略模式——这些在其他语言里需要特殊语法才能做到的事情,在 Go 里只是普通代码。


16.1 函数类型定义

16.1.1 参数列表

函数类型由它的参数列表和结果列表决定:

1
2
3
4
5
6
7
8
// 参数列表决定函数类型的签名
type AddFunc func(a, b int) int

// 使用
var add AddFunc = func(a, b int) int {
    return a + b
}
fmt.Println(add(1, 2))  // 3

AddFunc 是一个函数类型:接收两个 int,返回一个 int。所有满足这个签名的函数都可以赋值给 AddFunc 类型的变量。

16.1.2 结果列表

函数类型的结果列表也是类型签名的一部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
type Processor func(input string) (output string, err error)

// processor1 和 processor2 可以赋给 Processor 类型的变量
processor1 := func(input string) (string, error) {
    return strings.ToUpper(input), nil
}
processor2 := func(input string) (string, error) {
    return strings.ToLower(input), nil
}

var proc Processor = processor1
fmt.Println(proc("Hello"))  // HELLO <nil>

16.2 函数值

16.2.1 函数变量

函数可以像普通变量一样赋值、传递:

1
2
3
4
5
6
7
8
import "strings"

var toUpper func(string) string = strings.ToUpper
fmt.Println(toUpper("hello"))  // HELLO

// 函数变量之间可以互相赋值
toLower := toUpper  // 等等,这不对!toUpper 的类型是 func(string) string
// toLower := toUpper // 错误:类型不匹配!

16.2.2 nil 函数值

声明但未赋值的函数类型变量默认是 nil

1
2
3
4
5
var f func(int) int
fmt.Println(f == nil)  // true

// 调用 nil 函数会 panic
// f(1) // panic: call of nil function value

这意味着在调用函数值之前,一定要检查它是否为 nil

1
2
3
4
5
6
7
var f func(int) int

if f != nil {
    fmt.Println(f(10))
} else {
    fmt.Println("f is nil, skip calling")
}

16.3 函数比较

在 Go 里,函数值可以用 ==!= 比较,前提是两边都是同一个函数类型的变量

1
2
3
4
5
6
add := func(a, b int) int { return a + b }
add2 := add
add3 := func(a, b int) int { return a + b }

fmt.Println(add == add2)  // true — 指向同一个函数值
fmt.Println(add == add3)  // false — 不同函数实例(即使代码一样)

注意:如果两个函数是通过不同的字面量定义(即使代码完全相同),它们在 Go 里是不同的函数值,不能直接用 == 比较相等(编译错误)。函数比较主要用于检查两个函数变量是否指向同一个函数实例。


16.4 函数模式

16.4.1 回调模式

把函数作为参数传递给另一个函数,实现"回调":

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import "fmt"

type VisitFunc func(int)

// 对切片中每个元素执行回调
func visit(nums []int, fn VisitFunc) {
    for _, n := range nums {
        fn(n)
    }
}

nums := []int{1, 2, 3, 4, 5}

visit(nums, func(n int) {
    fmt.Printf("%d*2=%d\n", n, n*2)
})
// 1*2=2
// 2*2=4
// 3*2=6
// 4*2=8
// 5*2=10

16.4.2 策略模式

把不同的算法封装成函数,根据需要选择不同的策略:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
type SortStrategy func(a, b []int) []int

func bubbleSort(items []int) []int {
    // 冒泡排序实现...
    result := make([]int, len(items))
    copy(result, items)
    for i := 0; i < len(result); i++ {
        for j := 1; j < len(result)-i; j++ {
            if result[j] < result[j-1] {
                result[j], result[j-1] = result[j-1], result[j]
            }
        }
    }
    return result
}

func quickSort(items []int) []int {
    // 快速排序实现...
    if len(items) < 2 {
        return items
    }
    pivot := items[0]
    var left, right []int
    for _, v := range items[1:] {
        if v < pivot {
            left = append(left, v)
        } else {
            right = append(right, v)
        }
    }
    left = quickSort(left)
    right = quickSort(right)
    return append(append(left, pivot), right...)
}

func sortItems(items []int, strategy SortStrategy) []int {
    return strategy(items)
}

data := []int{5, 2, 8, 1, 9}
fmt.Println(sortItems(data, bubbleSort))  // 不同的策略
fmt.Println(sortItems(data, quickSort))   // 不同的策略

16.4.3 装饰器模式

用一个函数包装另一个函数,在其执行前后加上额外的行为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
type Handler func() string

// 装饰器:计时
func withTimer(next Handler) Handler {
    return func() string {
        start := time.Now()
        result := next()
        fmt.Printf("took %v\n", time.Since(start))
        return result
    }
}

// 装饰器:重试
func withRetry(next Handler) Handler {
    return func() string {
        for i := 0; i < 3; i++ {
            result := next()
            if result != "" {
                return result
            }
            fmt.Println("retry...", i+1)
        }
        return ""
    }
}

handler := func() string {
    return "done"
}

// 链式装饰
wrapped := withRetry(withTimer(handler))
fmt.Println(wrapped())

16.4.4 中间件模式

在 web 服务中,中间件就是一个接收 http.HandlerFunc、返回 http.HandlerFunc 的函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import (
    "fmt"
    "net/http"
    "time"
)

func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        fmt.Printf("%s %s took %v\n", r.Method, r.URL.Path, time.Since(start))
    }
}

func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if r.Header.Get("Authorization") == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        next.ServeHTTP(w, r)
    }
}

helloHandler := func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
}

// 应用中间件:先认证,再记录日志,最后执行业务逻辑
wrapped := loggingMiddleware(authMiddleware(helloHandler))
// http.HandleFunc("/hello", wrapped)
fmt.Println("Middleware chain ready!")

最后修改 March 20, 2026: 新增Go基础部分 (8cce995)