文章目录

Go 结构体:struct 与方法

发布于 2026-04-12 06:15:13 · 浏览 10 次 · 评论 0 条

Go 结构体:struct 与方法

Go 语言没有类,但结构体承担了面向对象编程中“类”的核心职责。结构体将不同类型的数据组合在一起,而方法则是定义在这些数据上的行为。掌握这两者是编写 Go 程序的基础。


1. 定义与初始化结构体

结构体是自定义数据类型的集合。你需要先定义类型,再创建实例。

定义一个名为 User 的结构体,包含 Name(字符串)和 Age(整数)两个字段。

type User struct {
    Name string
    Age  int
}

创建结构体实例有三种常见方式。

第一种,声明变量并使用字段名初始化。这种方式最清晰,字段顺序可以随意。

u1 := User{
    Name: "张三",
    Age:  25,
}

第二种,按照定义顺序初始化。这种方式要求值的顺序必须与结构体定义完全一致,少写或多写都会报错,容易出错,不推荐在字段多时使用。

u2 := User{"李四", 30}

第三种,使用 new 关键字。这会分配内存并返回指针,但字段的零值会被初始化。

u3 := new(User)
// u3 是 *User 类型
// u3.Name = "", u3.Age = 0

访问修改字段使用点号 . 操作符。

fmt.Println(u1.Name) // 输出: 张三
u1.Age = 26          // 修改年龄

2. 定义方法

方法是带有接收者的函数。它与普通函数的区别在于,它在 func 关键字和方法名之间增加了一个“参数”,即接收者。

定义一个 Greet 方法,让它属于 User 结构体。接收者放在 func 和方法名之间。

// (u User) 是接收者
func (u User) Greet() {
    fmt.Printf("你好,我是 %s\n", u.Name)
}

调用该方法时,使用点号操作符,就像访问字段一样。

u1.Greet() // 输出: 你好,我是 张三

3. 值接收者与指针接收者

这是 Go 方法中最关键的概念。选择哪种接收者,决定了方法能否修改原始数据。

3.1 值接收者

当接收者是结构体本身(如 u User)时,Go 会拷贝一份该结构体的副本。在方法内部对副本的任何修改,都不会影响原始数据。

func (u User) HaveBirthday() {
    u.Age++ // 这里只是修改了副本,原始 u1 的 Age 不会变
}

3.2 指针接收者

当接收者是指针(如 u *User)时,方法内部操作的是原始数据的内存地址。所有的修改都会直接影响原始对象。

func (u *User) HaveBirthday() {
    u.Age++ // 直接修改了原始对象
}

// 调用
u1.HaveBirthday()
fmt.Println(u1.Age) // 输出 26 (原来是 25)

为了更直观地理解两者的区别,请参考下表:

特性 值接收者 (u User) 指针接收者 (u *User)
数据操作 拷贝一份副本,操作副本 直接引用原内存地址
修改原对象 不会改变原始数据 会改变原始数据
适用场景 只读操作、小结构体 需要修改数据、大结构体
调用一致性 无论变量是值还是指针,都能调用 无论变量是值还是指针,都能调用

注意:Go 语言允许你直接用值变量调用指针接收者的方法,编译器会自动帮你取地址(&u1.HaveBirthday())。反之亦然,这降低了使用者的心智负担。


4. 嵌套与方法提升

Go 没有继承,但通过“结构体嵌套”可以实现代码复用。

定义一个 Address 结构体,并将其嵌入到 User 中。

type Address struct {
    City string
}

type User struct {
    Name string
    Address // 匿名嵌套
}

初始化并访问嵌套字段。

u := User{
    Name: "王五",
    Address: Address{
        City: "北京",
    },
}
fmt.Println(u.City) // 直接访问嵌套体的字段,输出: 北京

定义一个属于 Address 的方法。

func (a Address) ShowLocation() {
    fmt.Println("位于:", a.City)
}

调用该方法。因为 User 包含 Address,所以 User 实例可以直接调用 ShowLocation,这被称为“方法提升”。

u.ShowLocation() // 输出: 位于: 北京

如果 User 自己也定义了一个同名方法,那么 User 自己的方法会优先被调用,嵌套体的方法会被“屏蔽”。


5. 接口与结构体的协作

在 Go 中,接口是一组方法签名的集合。只要一个结构体实现了接口中定义的所有方法,我们就说它实现了该接口。这是隐式的,不需要显式声明 implements

定义一个 Speaker 接口,包含 Speak 方法。

type Speaker interface {
    Speak()
}

User 实现 Speaker 接口,只需添加对应方法。

func (u User) Speak() {
    fmt.Println(u.Name + " 正在说话。")
}

使用接口变量来接收结构体实例。

var s Speaker
s = u1 // u1 是 User 类型
s.Speak()

这种设计允许你编写只依赖行为(接口)而不依赖具体实现(结构体)的代码,极大地降低了代码耦合度。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文