Go语言方法和接收器类型详解

Go语言方法和接收器类型详解

1. 方法接收器类型

1.1 值接收器

值接收器方法不会改变接收器的状态,因为Go语言会在调用时复制接收器的值。因此,任何对接收器成员变量的修改都只会影响副本,而不会影响原始结构体实例。

go 复制代码
type Person struct {
    Name string
    Age  int
}

// 值接收器方法
func (p Person) GetAge() int {
    return p.Age
}

func TestValueReceiver(t *testing.T) {
    p := Person{Name: "Tom", Age: 20}
    if age := p.GetAge(); age != 20 {
        t.Errorf("Expected age 20, got %d", age)
    }
    // p的值在方法调用后不会改变
}

1.2 指针接收器

指针接收器允许直接操作接收器本身,这意味着可以安全地修改接收器的字段,并且这些更改会持续存在。

go 复制代码
// 指针接收器方法
func (p *Person) SetAge(age int) {
    p.Age = age
}

func TestPointerReceiver(t *testing.T) {
    p := &Person{Name: "Tom", Age: 20}
    p.SetAge(21)
    if p.Age != 21 {
        t.Errorf("Expected age 21, got %d", p.Age)
    }
    // p的值会被修改
}

2. 值类型和指针类型的方法调用

2.1 值类型调用指针接收器方法

go 复制代码
func TestValueTypeCallPointerMethod(t *testing.T) {
    p := Person{Name: "Tom", Age: 20}
    p.SetAge(21) // Go 自动转换为 (&p).SetAge(21)
    if p.Age != 21 {
        t.Errorf("Expected age 21, got %d", p.Age)
    }
}

2.2 指针类型调用值接收器方法

go 复制代码
func TestPointerTypeCallValueMethod(t *testing.T) {
    p := &Person{Name: "Tom", Age: 20}
    age := p.GetAge() // Go 自动转换为 (*p).GetAge()
    if age != 20 {
        t.Errorf("Expected age 20, got %d", age)
    }
}

3. 不可寻址的情况

3.1 Map值不可直接修改

go 复制代码
func TestMapValueNotAddressable(t *testing.T) {
    people := make(map[string]Person)
    people["tom"] = Person{Name: "Tom", Age: 20}
    
    // 错误做法
    // people["tom"].Age = 21
    
    // 正确的做法
    person := people["tom"]
    person.Age = 21
    people["tom"] = person
    
    if people["tom"].Age != 21 {
        t.Errorf("Expected age 21, got %d", people["tom"].Age)
    }
}

3.2 临时值不可寻址

go 复制代码
func TestTemporaryValueNotAddressable(t *testing.T) {
    // 以下代码会编译错误
    // Person{Name: "Tom", Age: 20}.SetAge(21)
    
    // 可以调用值接收器方法
    age := Person{Name: "Tom", Age: 20}.GetAge()
    if age != 20 {
        t.Errorf("Expected age 20, got %d", age)
    }
}

3.3 字面量不可寻址

go 复制代码
func TestLiteralNotAddressable(t *testing.T) {
    // 以下代码会编译错误
    // (&struct{ name string }{"tom"}).name = "jerry"
    
    // 正确的做法是先赋值给变量
    p := Person{Name: "Tom", Age: 20}
    p.SetAge(21)
    if p.Age != 21 {
        t.Errorf("Expected age 21, got %d", p.Age)
    }
}

3.4 Map值是指针类型的情况

go 复制代码
func TestMapWithPointerValues(t *testing.T) {
    // 创建一个字符串到 *Person 的映射
    people := make(map[string]*Person)
    
    // 添加一个新的 Person 到映射中
    people["tom"] = &Person{Name: "Tom", Age: 20}
    
    // 直接修改 tom 的年龄
    people["tom"].Age = 21
    
    if people["tom"].Age != 21 {
        t.Errorf("Expected age 21, got %d", people["tom"].Age)
    }
}

4. Map值的正确修改方式

  • 获取值的副本
  • 修改副本
  • 将修改后的副本存回map

5. 方法调用的自动转换

5.1 值到指针的自动转换

  • Go编译器自动处理从值到指针的转换
  • 允许值类型调用指针接收器方法

5.2 指针到值的自动转换

  • Go编译器自动处理从指针到值的转换
  • 允许指针类型调用值接收器方法

注意事项

  1. 选择接收器类型时考虑:

    • 是否需要修改接收器状态
    • 性能考虑
    • 接口实现要求
  2. 处理不可寻址情况:

    • 使用中间变量
    • 正确处理map值的修改
    • 注意指针类型的使用
  3. 使用map存储指针时:

    • 可以直接修改指针指向的对象
    • 确保指针正确初始化
    • 考虑并发安全问题
相关推荐
bagadesu2 分钟前
28.<Spring博客系统⑤(部署的整个过程
java·后端
百锦再5 分钟前
第14章 智能指针
android·java·开发语言·git·rust·go·错误
zhong liu bin11 分钟前
Java并发编程【JUC】【一】
java·开发语言·juc
fire-flyer33 分钟前
设计模式之观察者模式
java·开发语言
倚栏听风雨40 分钟前
git "base点"详解,顺便解释merge和rebase区别
后端
咬_咬1 小时前
C++仿muduo库高并发服务器项目:Poller模块
服务器·开发语言·c++·epoll·muduo
掘金一周1 小时前
重新思考 weapp-tailwindcss 的未来 | 掘金一周 11.13
前端·人工智能·后端
Highcharts.js1 小时前
在Python中配置高度交互的数据可视化:Highcharts完全指南
开发语言·python·信息可视化·highcharts
小生凡一1 小时前
图解|Go语言实现 Agent|LLM+MCP+RAG
开发语言·后端·golang
l0sgAi1 小时前
SpringAI 整合MCP实现联网搜索 (基于tavily)
java·后端