# 3.6、Golang 结构体 struct ## 类型定义和类型别名 类型定义的语法 ```go type NewType Type ``` 示例 ```go package main import "fmt" func main() { // 定义类型 type MyInt int var i MyInt i = 100 fmt.Printf("%T", i) // main.MyInt } ``` 类型别名语法 ```go type NewType = Type ``` 示例 ```go package main import "fmt" func main() { // 定义类型别名 type MyInt = int var i MyInt i = 100 fmt.Printf("%T", i) // int } ``` 类型定义和类型别名的区别 - 类型定义相当于定义了一个全新的类型 - 类型别名只会在代码中存在,编译完成后并不会存在 - 类型别名可以使用原来类型所有的方法 ## Golang结构体 golang中没有面向对象的概念,但是可以使用结构体来实现,面向对象编程的一些特性,例如:继承、组合等 结构体的定义 ```go type struct_variable_type struct { // 成员 member definition; member definition; ... member definition; } ``` 示例 ```go // 定义一个结构体Person type Person struct { id int name string age int email string } // 可以把相同类型的成员合并到一行 type Person struct { id, age int name, email string } ``` 声明结构体变量 ```go var tom Person fmt.Printf("%v", tom) // {0 0 } jack := Person{} fmt.Printf("%v", jack) // {0 0 } ``` 访问结构体成员 ```go package main import "fmt" // 定义一个结构体Person // 可以把相同类型的成员合并到一行 type Person struct { id, age int name, email string } func main() { var tom Person tom.id = 12 tom.name = "Tom" tom.age = 23 tom.email = "123@qq.com" fmt.Printf("%v", tom) // {12 23 Tom 123@qq.com} } ``` 匿名结构体 ```go package main import "fmt" func main() { var dog struct { id int name string } dog.id = 12 dog.name = "Tom" fmt.Printf("%v", dog) // {12 Tom} } ``` ## Golang结构体的初始化 未初始化的结构体,成员都是零值 使用键值对的方式初始化 ```go package main import "fmt" // 定义一个结构体Person type Person struct { id int age int name string email string } func main() { tom := Person{ id: 12, age: 23, name: "Tom", email: "123@qq.com", } fmt.Printf("%v", tom) // {12 23 Tom 123@qq.com} } ``` 使用值的列表初始化 ```go package main import "fmt" // 定义一个结构体Person type Person struct { id int age int name string email string } func main() { tom := Person{ 12, 23, "Tom", "123@qq.com", } fmt.Printf("%v", tom) // {12 23 Tom 123@qq.com} } ``` 部分成员初始化 ```go package main import "fmt" // 定义一个结构体Person type Person struct { id int age int name string email string } func main() { tom := Person{ id: 12, name: "Tom", } fmt.Printf("%v", tom) // {12 0 Tom } } ``` ## Golang结构体的指针 普通指针 ```go package main import "fmt" // 定义一个结构体Person type Person struct { id int age int name string email string } func main() { // 定义字符串变量 var name string name = "Tom" // 定义字符串类型的指针变量 var name_ptr *string name_ptr = &name // 变量的值 fmt.Printf("%v\n", name) // Tom // 指针地址 fmt.Printf("%v\n", name_ptr) // 0xc000014250 // 指针指向的变量值 fmt.Printf("%v\n", *name_ptr) // Tom } ``` 结构体的指针 ```go package main import "fmt" // 定义一个结构体Person type Person struct { id int age int name string email string } func main() { // 结构体变量 person := Person{ id: 11, name: "Tom", age: 23, email: "123@qq.com", } // 指针变量 var person_ptr *Person person_ptr = &person // 变量的值 fmt.Printf("%v\n", person) // {11 23 Tom 123@qq.com} // 指针地址 fmt.Printf("%p\n", person_ptr) // 0xc00009a180 // 指针指向的变量值 fmt.Printf("%v\n", *person_ptr) // {11 23 Tom 123@qq.com} } ``` 使用new关键字创建结构体指针 ```go package main import "fmt" // 定义一个结构体Person type Person struct { id int age int name string email string } func main() { var person = new(Person) // 访问结构体指针成员,可以省略* // 自动解引用 (*person).age = 23 person.id = 12 person.name = "Tom" fmt.Printf("%T\n", person) // *main.Person fmt.Printf("%v\n", person) // &{12 0 Tom } fmt.Printf("%v\n", *person) // {12 0 Tom } } ``` ## 结构体作为函数参数 结构体作为参数传递给函数 - 直接传递结构体,传递的是副本,函数内部不能改变外部结构体内容 - 传递结构体指针,函数内部能改变外部结构体内容 值传递 ```go package main import "fmt" type Person struct { age int name string } // 值传递,拷贝了一份副本 func showPerson(person Person) { person.age = 12 person.name = "Jakc" fmt.Printf("%v\n", person) } func main() { person := Person{ age: 11, name: "Tom", } fmt.Printf("%v\n", person) // {11 Tom} showPerson(person) // {12 Jakc} fmt.Printf("%v\n", person) // {11 Tom} } ``` 指针传递 ```go package main import "fmt" type Person struct { age int name string } // 指针传递 func showPerson(person *Person) { person.age = 12 person.name = "Jakc" fmt.Printf("%v\n", *person) } func main() { person := Person{ age: 11, name: "Tom", } fmt.Printf("%v\n", person) // {11 Tom} showPerson(&person) // {12 Jakc} fmt.Printf("%v\n", person) // {12 Jakc} } ``` ## 结构体的嵌套 示例 ```go package main import "fmt" type Dog struct { age int name string } type Person struct { age int name string dog Dog } func main() { dog := Dog{ age: 1, name: "dog", } person := Person{ dog: dog, age: 11, name: "Tom", } fmt.Printf("%v\n", person) // {11 Tom {1 dog}} fmt.Printf("%v\n", person.dog.name) // dog } ``` ## 方法 golang中,方法是一种特殊的函数 定义与struct之上(与struct关联、绑定),被称为struct的接受者(receiver) 通俗讲,方法就是有接收者的函数 语法格式 ```go type struct_name struct{} func (recv struct_name) method_name(param_type) return_type{} func (recv *struct_name) method_name(param_type) return_type{} ``` ```go package main import "fmt" // 属性和方法分开写 type Person struct { age int name string } // Person 接收者 receiver func (person Person) eat() { fmt.Printf("%v eat\n", person.name) } func (person Person) sleep() { fmt.Printf("%v sleep\n", person.name) } func main() { person := Person{ age: 11, name: "Tom", } person.eat() // Tom eat person.sleep() // Tom sleep } ``` go语言方法的注意事项 - 方法的receiver type可以是类型别名、struct、slice、map、channel、func等 - struct结合它的方法等价于面向对象中的类,只不过struct可以和它的方法分开写,并非一定要属于同一个文件,但是,必须属于同一个包 - 方法有两种接收类型`(T type)` 和 `(T *Type)`,它们之间有区别 - 方法就是函数,所有Go中没有方法重载overload的说法,也就是说同一个类型中的所有方法名必须唯一 - 如果receiver是一个指针类型,则会自动解除引用 - 方法和type是分开的,意味着实例的行为behavior和数据存储field是分开的,但它们通过receiver建立起关联关系 ## 方法接收者类型 - 值类型:复制结构体副本 - 指针类型:不复制 ```go package main import "fmt" // Person 接收者 receiver type Person struct { age int name string } // 值传递,拷贝了一份副本 func (person Person) showPersonByValue() { person.age = 12 person.name = "Jack" fmt.Printf("%v\n", person) } // 指针传递 func (person *Person) showPersonByPointer() { person.age = 12 person.name = "Jack" fmt.Printf("%v\n", person) } func main() { person := Person{ age: 11, name: "Tom", } fmt.Printf("%v\n", person) // {11 Tom} // 接收者类型是变量,值没有被修改 person.showPersonByValue() // {12 Jack} fmt.Printf("%v\n", person) // {11 Tom} // 接收者类型是指针,值被修改 person.showPersonByPointer() // &{12 Jack} fmt.Printf("%v\n", person) // {12 Jack} } ```