Go 语言结构体
定义、字面量、嵌入(组合)、标签、以及结构体比较。
Key PointsGo 用结构体 + 嵌入实现组合,没有继承;嵌入的方法会被「提升」到外层。
定义与使用
Go
type User struct {
ID int
Name string
Email string
Age int
}
u := User{ID: 1, Name: "张三", Age: 20}
fmt.Println(u)
fmt.Println(u.Name)
fmt.Printf("%+v\n", u) // 带字段名Output
{1 张三 20}
张三
{ID:1 Name:张三 Email: Age:20}指针与零值
Go
var u User // 所有字段都是零值
fmt.Printf("%+v\n", u)
p := &User{Name: "李四"} // 指针,字段自动解引用
p.Age = 30
fmt.Println(p.Age, p.Name)Output
{ID:0 Name: Email: Age:0}
30 李四嵌入(组合)
Go
type Base struct {
ID int
CreatedAt string
}
func (b Base) Describe() string {
return fmt.Sprintf("ID=%d", b.ID)
}
type Article struct {
Base // 嵌入,字段和方法被提升
Title string
}
a := Article{Base: Base{ID: 7}, Title: "Go 入门"}
fmt.Println(a.ID) // 提升字段
fmt.Println(a.Describe()) // 提升方法Output
7
ID=7Embedding is not inheritance: Article has a Base, it is not a Base. You cannot assign an Article to a Base variable, and embedding gives no polymorphism — that is what interfaces are for.
方法
Go
func (u User) Full() string {
return u.Name + " <" + u.Email + ">"
}
func (u *User) SetName(n string) {
u.Name = n
}
u := User{Name: "张三", Email: "z@example.com"}
u.SetName("张三丰") // 编译器自动取地址
fmt.Println(u.Full())Output
张三丰 <z@example.com>标签
Go
type Login struct {
User string `json:"user" form:"user" validate:"required"`
Pwd string `json:"-"` // 序列化时忽略
}
b, _ := json.Marshal(Login{User: "tom", Pwd: "secret"})
fmt.Println(string(b))Output
{"user":"tom"}Tags are string metadata read via reflection. json, yaml, db and validate all depend on them. A typo compiles fine and fails silently at runtime — the kind of bug that eats an afternoon.
比较
Go
a := User{ID: 1, Name: "x"}
b := User{ID: 1, Name: "x"}
fmt.Println(a == b) // 所有字段都可比较时才能 ==
// 含 slice / map / func 字段的结构体不能比较
type Bad struct{ Tags []string }
// fmt.Println(Bad{} == Bad{}) // 编译错误Output
trueStructs with
[]T, map or func fields cannot use == or be map keys. Use reflect.DeepEqual or write an Equal method (faster and clearer).定义与使用
Go
type User struct {
ID int
Name string
Email string
Age int
}
u := User{ID: 1, Name: "张三", Age: 20}
fmt.Println(u)
fmt.Println(u.Name)
fmt.Printf("%+v\n", u) // 带字段名Output
{1 张三 20}
张三
{ID:1 Name:张三 Email: Age:20}指针与零值
Go
var u User // 所有字段都是零值
fmt.Printf("%+v\n", u)
p := &User{Name: "李四"} // 指针,字段自动解引用
p.Age = 30
fmt.Println(p.Age, p.Name)Output
{ID:0 Name: Email: Age:0}
30 李四嵌入(组合)
Go
type Base struct {
ID int
CreatedAt string
}
func (b Base) Describe() string {
return fmt.Sprintf("ID=%d", b.ID)
}
type Article struct {
Base // 嵌入,字段和方法被提升
Title string
}
a := Article{Base: Base{ID: 7}, Title: "Go 入门"}
fmt.Println(a.ID) // 提升字段
fmt.Println(a.Describe()) // 提升方法Output
7
ID=7嵌入不是继承:Article 有一个 Base,不是 Article 是 Base。区别在于不能把 Article 直接赋给 Base 类型的变量,也不能用嵌入做多态——那是接口的事。
方法
Go
func (u User) Full() string {
return u.Name + " <" + u.Email + ">"
}
func (u *User) SetName(n string) {
u.Name = n
}
u := User{Name: "张三", Email: "z@example.com"}
u.SetName("张三丰") // 编译器自动取地址
fmt.Println(u.Full())Output
张三丰 <z@example.com>标签
Go
type Login struct {
User string `json:"user" form:"user" validate:"required"`
Pwd string `json:"-"` // 序列化时忽略
}
b, _ := json.Marshal(Login{User: "tom", Pwd: "secret"})
fmt.Println(string(b))Output
{"user":"tom"}标签是给反射读的字符串元数据。json、yaml、db、validate 都靠它工作。写错了编译器不报错,只在运行时静默失效——这是排查耗时的地方。
比较
Go
a := User{ID: 1, Name: "x"}
b := User{ID: 1, Name: "x"}
fmt.Println(a == b) // 所有字段都可比较时才能 ==
// 含 slice / map / func 字段的结构体不能比较
type Bad struct{ Tags []string }
// fmt.Println(Bad{} == Bad{}) // 编译错误Output
true含
[]T、map、func 字段的结构体不能用 ==,也不能当 map 的 key。需要比较时用 reflect.DeepEqual 或自己写 Equal 方法(后者更快、更明确)。