结构体
和 C 语言一样,Go 也有 struct
,与数组一样属于复合类型,并非引用类型,与其他面向对象编程语言中的类一样可以定义字段(属性)和方法,但也有很不同的地方。
定义
使用 struct 关键字可以定义一个结构体,结构体中的成员称为结构体的字段或属性。
type Member struct {
ID int
Name string
Email string
Gender int
}
当然你也可以这么定义。
type Member struct {
ID int
Name, Email string
Gender int
}
结构体可以不包含任何字段,称为空结构体,struct{}
表示一个空的结构体,不过直接定义一个空的结构体并没有什么意义。 但在 channel 之间的通讯中可以用一个空结构体作为信号量。
ch := make(chan struct{})
ch <- struct{}{}
使用
- 直接定义变量,不会为字段赋初始值,所有字段都会赋默认值。
var m Member
- 使用字面量创建变量
m := Member {
1,
"小明",
"xiaoming@email.com",
1,
}
m := Member {
id: 2,
"name": "小红",
}
- 访问字段
fmt.Println(m.ID)
m.Name = "小花"
- 指针结构体
结构体与数组一样都是值传递,比如当把数组或结构体作为实参传给函数的形参时,会复制一个副本。 所以为了提高性能,一般把结构体传给函数时使用指针结构体。
func main() {
m := NewMember("小明")
fmt.Println(m.name)
}
func NewMember(name string) *Member {
return &Member{
"name": name,
}
}
可见性
在 Go 语言中,无论是结构体还是结构体内的字段又或是函数、常变量,它们的可见性都是由首字母的大小写决定的。
package member
// 结构体对外可见
type Member struct {
ID int // 字段对外可见
Name string // 字段对外可见
Email string // 字段对外可见
gender int // 字段对外不可见
}
// 结构体对外可见
type member struct {
ID int // 字段对外不可见
Name string // 字段对外不可见
Email string // 字段对外不可见
gender int // 字段对外不可见
}
两个一样的结构体,就因为结构体名不同,就会影响它们的可见性。 在代码中可以看到,如果一个结构体对外不可见,那么就算它的字段是大写的也不会对外可见。 值得一提的是,Go 语言支持(UTF-8 编码)中文变量、函数名,但是它们都是不可见的(中文是小写系列)。
变量 := 123
fmt.Println(变量)
Tags
在定义结构体字段时,可以使用反引号为结构体字段声明元信息,用于编译阶段关联到字段当中。
type Member struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Gender int `json:"gender"`
}
tag 的使用是函数定义的,上面定义的信息是标准库 encoding/json
编解码使用的。
type Member struct {
ID int `json:"id, -"`
Name string `json:"name"`
Email string `json:"email"`
Gender int `json:"gender"`
}
这些信息不仅是一个字符串那么简单,因为其主要用于反射场景,reflect
包中提供了操作方法,所以写法也要遵循一定的规则。
Tag 本身是一个字符串,但字符串中却是:以空格分隔的 key:value 对。
- key: 必须是非空字符串,字符串不能包含控制字符、空格、引号、冒号。
- value: 以双引号标记的字符串
- 冒号前后不能有空格
type Server struct {
ServerName string `key1: "value1" key11:"value11"`
ServerIP string `key2: "value2"`
}
tag 的用途很广泛,常见一点就是 json 编解码,orm 框架等等。
基本概念
- 结构体是复合类型,无论是作为实参传递给函数时,还是赋值给其他变量,都是值传递。
- Go 是支持面向对象的,但却没有继承的概念,在结构体中,可以通过组合其他结构体来构建更复杂的结构体。
- 结构体不能包含自己,但可以包含自己的指针
方法
在 Go 语言中,将函数绑定到具体的类型中,则称该函数是该类型的方法。
func NewMember(name string) *Member {
return &Member{
"name": name,
}
}
func (m *Member) setName(name string) {
m.Name = name
}
- 调用结构体的方法,与调用字段一样
m := Member{} m.setName("小明")
组合
Go 语言的结构体没有继承的概念,但是有组合的概念,通过这种方式来达到代码复用效果。
组合可以理解为定义一个结构体中,其字段可以是其他的结构体,这样,不同的结构体就可以共用相同的字段。
例如,我们定义了一个名为Animal表示动物,如果我们想定义一个结构体表示猫,如:
type Animal struct {
Name string
}
func (a Animal) Run() {
fmt.Println(a.Name + "is running")
}
func (a Animal) Eat() {
fmt.Println(a.Name + "is eating")
}
type Cat struct {
a Animal
}
func main() {
c := Cat {
a: Animal {
Name: "猫",
},
}
fmt.Println(c.a.Name)
c.a.Run()
}
匿名字段
上例中,Animal
作为 Cat
的字段时的变量名为 a
,所以调用方法是 c.a.Run()
。
Go 语言支持直接将类型作为结构体的字段,而不需要取变量名,这种字段叫匿名字段。
type Lion struct {
Animal // 匿名字段
}
func main(){
var lion = Lion{
Animal{
Name: "狮子",
},
}
lion.Run()
fmt.Println(lion.Name)
}