Go 函数与方法

00:00
3 min Beginner 2026/6/14

函数定义、多返回值、命名返回值、可变参数、init 函数、方法与接收者、接口与隐式实现。

1. 函数定义

1.1 基本语法

// 函数声明格式:func 函数名(参数列表) (返回值列表) { 函数体 }
func add(a int, b int) int {
    return a + b
}

// 相同类型的参数可以合并类型声明
func add(a, b int) int {
    return a + b
}

// 调用
result := add(1, 2) // 3

1.2 多返回值

Go 函数支持多返回值,这是错误处理的基础模式:

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// 调用时必须处理所有返回值
result, err := divide(10, 3)
if err != nil {
    fmt.Println("Error:", err)
    return
}
fmt.Println(result)

1.3 命名返回值

// 命名返回值相当于在函数开头声明了变量
func rectangleProps(w, h float64) (area, perimeter float64) {
    area = w * h
    perimeter = 2 * (w + h)
    return // 裸 return,自动返回命名变量
}

// 也可显式返回
func swap(a, b int) (first, second int) {
    first = b
    second = a
    return
}

建议:简短函数使用命名返回值提高可读性;长函数建议显式 return,避免混淆。

1.4 可变参数

// 可变参数在函数内被视为切片
func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

fmt.Println(sum(1, 2, 3))       // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15

// 展开切片
numbers := []int{10, 20, 30}
fmt.Println(sum(numbers...))     // 60

// 可变参数与固定参数混合
func printf(format string, args ...any) {
    fmt.Printf(format, args...)
}

1.5 函数是一等公民

// 函数变量
add := func(a, b int) int {
    return a + b
}
fmt.Println(add(1, 2)) // 3

// 高阶函数:函数作为参数
func apply(nums []int, fn func(int) int) []int {
    result := make([]int, len(nums))
    for i, n := range nums {
        result[i] = fn(n)
    }
    return result
}

doubled := apply([]int{1, 2, 3}, func(n int) int {
    return n * 2
})
// doubled = [2, 4, 6]

// 高阶函数:函数作为返回值
func multiplier(factor int) func(int) int {
    return func(n int) int {
        return n * factor
    }
}

double := multiplier(2)
triple := multiplier(3)
fmt.Println(double(5)) // 10
fmt.Println(triple(5)) // 15

1.6 闭包

闭包是引用了外部变量的匿名函数:

func counter() func() int {
    count := 0
    return func() int {
        count++ // 捕获并修改外部变量
        return count
    }
}

c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3

c2 := counter()
fmt.Println(c2()) // 1(独立的闭包实例)

闭包经典陷阱

// 错误:所有闭包共享同一个 i
func wrong() {
    for i := 0; i < 3; i++ {
        go func() {
            fmt.Println(i) // 可能全部输出 3
        }()
    }
}

// 正确:通过参数传递
func right() {
    for i := 0; i < 3; i++ {
        go func(n int) {
            fmt.Println(n) // 0, 1, 2(顺序不确定)
        }(i)
    }
}

2. init 函数

init 函数在每个包中自动执行,用于包的初始化:

package config

var version string

func init() {
    // 自动执行,无法被调用
    version = "1.0.0"
    loadConfig()
}

func init() {
    // 可以有多个 init 函数,按声明顺序执行
    validateConfig()
}

init 执行顺序

  1. 被导入的包先初始化(递归)
  2. 包级变量初始化
  3. init() 函数声明顺序执行
  4. 文件中的 init文件母序执行

最佳实践:尽量减少 init 的使用,偏好显式初始化函数

3. 方法与接收者

3.1 方法定义

方法绑定到特定类型函数通过接收声明

type Circle struct {
    Radius float64
}

// 值接收者
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

// 指针接收者
func (c *Circle) Scale(factor float64) {
    c.Radius *= factor
}

func main() {
    c := Circle{Radius: 5}
    fmt.Println(c.Area())   // 78.54
    c.Scale(2)
    fmt.Println(c.Radius)   // 10
}

3.2 值接收者 vs 指针接收者

特性接收者 func (t T)指针接收者 func (t *T)
修改接收不能修改原始值可以修改原始值
拷贝每次调用拷贝整个只拷贝指针(8 字节
调用方式t.Method()p.Method()t.Method()p.Method()
适用场景小型不可变类型需要修改或大型结构体
type User struct {
    Name string
    Age  int
}

// 值接收者:不修改原始值
func (u User) Greet() string {
    return "Hello, " + u.Name
}

// 指针接收者:修改原始值
func (u *User) Birthday() {
    u.Age++
}

func main() {
    u := User{Name: "Alice", Age: 30}

    fmt.Println(u.Greet()) // Hello, Alice

    u.Birthday()
    fmt.Println(u.Age)     // 31

    // 指针也能调用值接收者方法(自动解引用)
    p := &u
    fmt.Println(p.Greet()) // Hello, Alice

    // 值也能调用指针接收者方法(自动取地址)
    u.Birthday() // 等价于 (&u).Birthday()
}

规则:如果一个类型指针接收者方法,所有方法都应使用指针接收者,保持一致性

3.3 任何类型都可以有方法

// 自定义类型
type Celsius float64

func (c Celsius) ToFahrenheit() float64 {
    return float64(c)*9/5 + 32
}

func (c Celsius) String() string {
    return fmt.Sprintf("%.1f°C", c)
}

boiling := Celsius(100)
fmt.Println(boiling.ToFahrenheit()) // 212.0
fmt.Println(boiling)                 // 100.0°C(自动调用 String)

// 不能给其他包的类型定义方法
// func (s string) MyMethod() {} // 编译错误
// 解决方案:定义自定义类型
type MyString string
func (s MyString) Shout() string {
    return strings.ToUpper(string(s)) + "!!!"
}

3.4 方法值与方法表达式

type Rect struct{ W, H float64 }

func (r Rect) Area() float64 { return r.W * r.H }

r := Rect{W: 3, H: 4}

// 方法值:绑定接收者
area := r.Area
fmt.Println(area()) // 12

// 方法表达式:未绑定接收者
areaFn := Rect.Area
fmt.Println(areaFn(r)) // 12

4. 接口与隐式实现

4.1 接口定义

type Speaker interface {
    Speak() string
}

4.2 隐式实现

Go 中类型无需显式声明实现接口,只要实现接口的所有方法即可:

type Dog struct{ Name string }

func (d Dog) Speak() string {
    return d.Name + " says: Woof!"
}

type Robot struct{ ID int }

func (r Robot) Speak() string {
    return fmt.Sprintf("Robot #%d says: Beep!", r.ID)
}

// 两者都隐式实现了 Speaker 接口
func makeItSpeak(s Speaker) {
    fmt.Println(s.Speak())
}

makeItSpeak(Dog{Name: "Rex"})    // Rex says: Woof!
makeItSpeak(Robot{ID: 42})       // Robot #42 says: Beep!

4.3 接口组合

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 接口组合
type ReadWriter interface {
    Reader
    Writer
}

// 结构体也可以组合接口
type FileReader struct {
    Reader // 嵌入接口
}

4.4 空接口

interface{}(Go 1.18+ 可写any)不含任何方法,所有类型实现了它:

func printAny(v any) {
    fmt.Println(v)
}

printAny(42)
printAny("hello")
printAny([]int{1, 2, 3})
printAny(struct{}{})

// 常见用途:JSON 解析
var data any
json.Unmarshal([]byte(`{"key": "value"}`), &data)

4.5 类型断言与类型开关

var s Speaker = Dog{Name: "Rex"}

// 类型断言
dog, ok := s.(Dog) // ok 为 true 表示断言成功
if ok {
    fmt.Println(dog.Name)
}

// 类型开关
func classify(v any) string {
    switch v := v.(type) {
    case int:
        return fmt.Sprintf("int: %d", v)
    case string:
        return fmt.Sprintf("string: %s", v)
    case bool:
        return fmt.Sprintf("bool: %t", v)
    default:
        return fmt.Sprintf("unknown: %T", v)
    }
}

5. 函数设计模式

5.1 函数选项模式(Functional Options)

type Server struct {
    host    string
    port    int
    timeout time.Duration
}

type Option func(*Server)

func WithHost(host string) Option {
    return func(s *Server) { s.host = host }
}

func WithPort(port int) Option {
    return func(s *Server) { s.port = port }
}

func WithTimeout(timeout time.Duration) Option {
    return func(s *Server) { s.timeout = timeout }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        host: "localhost",
        port: 8080,
        timeout: 30 * time.Second,
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

// 使用
srv := NewServer(
    WithHost("0.0.0.0"),
    WithPort(3000),
    WithTimeout(10 * time.Second),
)

5.2 中间件模式

type Handler func(msg string)

func LoggingMiddleware(next Handler) Handler {
    return func(msg string) {
        log.Printf("Before: %s", msg)
        next(msg)
        log.Printf("After: %s", msg)
    }
}

func TimingMiddleware(next Handler) Handler {
    return func(msg string) {
        start := time.Now()
        next(msg)
        log.Printf("Took: %v", time.Since(start))
    }
}

// 链式调用
handler := LoggingMiddleware(TimingMiddleware(func(msg string) {
    fmt.Println("Processing:", msg)
}))

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式