Go语言之匿名字段与组合 -《Go语言实战指南》

144 阅读2分钟

Go 没有传统的面向对象继承机制,但它通过“匿名字段(embedding) ”实现了类似继承的组合方式,使得一个类型可以“继承”另一个类型的字段和方法。


一、什么是匿名字段

匿名字段就是在结构体中嵌套一个类型而不显式命名字段名。该字段的名字默认就是其类型名。

示例:

type Person struct {
    Name string
    Age  int
}

type Student struct {
    Person     // 匿名字段,嵌入 Person
    SchoolName string
}

这里的 Student 类型“组合”了 Person 类型,它自动获得了 Person 的字段和方法访问权限。


二、使用嵌入字段

s := Student{
    Person:     Person{Name: "Alice", Age: 20},
    SchoolName: "Go大学",
}

fmt.Println(s.Name) // 自动访问嵌入字段的字段:Alice
fmt.Println(s.Age)  // 20

实际上等价于:

fmt.Println(s.Person.Name)

三、方法提升(Method Promotion)

如果嵌入的类型定义了方法,外层类型会自动拥有这些方法。

func (p Person) SayHi() {
    fmt.Println("Hi, I'm", p.Name)
}

s := Student{Person: Person{Name: "Tom"}}
s.SayHi() // 自动继承 Person 的方法

四、指针匿名字段

也可以嵌入指针类型:

type Teacher struct {
    *Person  // 匿名嵌入指针
    Subject string
}

只要嵌入类型是指针或值,Go 都会自动处理方法调用的自动解引用。


五、字段名冲突的情况

如果外部结构体和匿名字段中存在同名字段或方法,优先使用外层结构体的成员

type Person struct {
    Name string
}

type Employee struct {
    Person
    Name string // 会隐藏 Person.Name
}

e := Employee{
    Person: Person{Name: "Tom"},
    Name:   "Jerry",
}
fmt.Println(e.Name)       // Jerry
fmt.Println(e.Person.Name) // Tom

六、嵌套多个匿名字段

可以同时嵌入多个匿名字段,实现多重组合:

type Contact struct {
    Email string
    Phone string
}

type Profile struct {
    Person
    Contact
}

调用:

p := Profile{
    Person:  Person{Name: "Lily", Age: 28},
    Contact: Contact{Email: "lily@example.com", Phone: "123456"},
}
fmt.Println(p.Email) // 直接访问 Contact.Email

七、小结

特性描述
匿名字段嵌入类型,无需字段名,字段名自动为类型名
方法提升嵌入类型的方法会自动“继承”
字段提升可直接访问嵌入类型的字段
字段冲突优先级外层结构体字段优先
支持指针/值嵌入嵌入字段可为值或指针类型,方法访问自动解引用
多重组合可以嵌套多个匿名字段,支持结构体“组合式编程”