结构体和方法
结构
- 结构把有内在联系的不同类型的数据统一成一个整体,使它们相互关联
- 结构是变量的集合,从外部看是一个实体
- 结构支持嵌套
- 结构的字段类型不限
- 结构的存储空间连续,按声明时的顺序存放
结构定义
- 命名类型结构
- 匿名类型结构
type Employee struct {
firstName string
lastName string
age int
salary int
}
//等价于
type Employee struct {
firstName, lastName string
age, salary int
}
//匿名类型结构
var myemployee struct {
firstName, lastName string
age, salary int
}
//带标签(tag)的结构体
//用来描述字段的信息
type User struct {
UserName string "用户名称"
UserId int `json:"user_id" bson:"b_user_id"`
}
结构变量初始化
用命名类型结构或匿名类型结构声明的结构变量,各字段初始化为相关类型的零值
用字段名初始化,不用按顺序,未指定的字段为零值
用字面量初始化,按字段类型声明顺序并全部设置,顺序不对或遗漏字段报错
emp1 := Employee{
firstName: "Sam",
age: 25,
salary: 500,
lastName: “Anderson”, //逗号不能忽略
}
//报错
emp2 := Employee{"Thomas", "Paul", 29, 800}
访问和修改结构变量的各个字段
//采用 结构变量.字段
emp := Employee{"Thomas", "Paul", 29, 800}
fmt.Println(emp.age)
//采用(*结构变量指针).字段
emp := &Employee{"Sam", "Anderson", 55, 6000}
fmt.Println("First Name:", (*emp).firstName)
//采用 结构变量指针.字段,不支持->
emp := &Employee{"Sam", "Anderson", 55, 6000}
fmt.Println("First Name:", emp.firstName)
//匿名字段
type Person struct {
string
int
}
p := Person{"Naveen", 50}
p.int =60
//匿名字段
//结构体字段也可以省略字段名,字段名默认为对应数据类型名称(数据类型不能重复)
type Person struct {
string
int
}
p := Person{"Naveen", 50}
p.int =60
嵌套字段和子结构字段提升
子结构字段提升(没有同名父结构字段)
type Address struct {
city, state string
}
type Person struct {
name string
age int
address Address
}
func main() {
var p Person
p.name = "Naveen"
p.age = 50
p.address = Address{
city: "Chicago",
state: "Illinois",
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.address.city)
fmt.Println("State:", p.address.state)
}
//子结构字段提升(没有同名父结构字段)
type Address struct {
city, state string
}
type Person struct {
name string
age int
Address //子结构匿名
}
func main() {
var p Person
p.name = "Naveen"
p.age = 50
p.Address = Address{
city: "Chicago",
state: "Illinois",
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println(“City:”, p.city) //city 提升
fmt.Println(“State:”, p.state) //state 提升
}
方法
- 方法是对具体类型行为的封装,本质上是绑定到该类型的函数
- 非命名类型不能定义方法
- OO语言的方法通常有个隐藏的this或self指针来指向对象
- GO 把这个隐藏指针暴露出来,称为接收者(receiver)
- 接收者可自定义名称,语法格式:
- 值类型 func (t Type) funcName(paramList)(resultList)
- 指针func (t *Type) funcName(paramList)(resultList)
type Employee struct {
name string
salary int
currency string
}
//定义方法
func (e Employee) displaySalary() {
fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
func main() {
emp1 := Employee{
name: "Sam Adolf",
salary: 5000,
currency: "$",
}
emp1.displaySalary()
}
//方法可用等价函数实现
type Employee struct {
name string
salary int
currency string
}
func displaySalary(e Employee) {
fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
func main() {
emp1 := Employee{
name: "Sam Adolf",
salary: 5000,
currency: "$",
}
displaySalary(emp1)
}
why?方法可用等价函数实现,为什么还要方法
- GO 的函数不能重载,导致不同类型不能用同名函数,而不同类型的方法可以同名
- GO 不支持class,使用结构代替类,结构字段用来封装对象属性,方法用来封装对象的行为
方法接收者(重要)
- 方法接收者的本质是形参
- 方法接收者为值时,方法修改对象属性将不能成功
- 方法接收者为值时,需要在内存复制一份对象,效率低
package main
import "fmt"
type MyStruct struct {
a int
b int
c int
}
func (item MyStruct) add() {
item.c = item.a + item.b
}
func (itemPoint *MyStruct) p_add() {
itemPoint.c = itemPoint.a + itemPoint.b
}
func main() {
item := MyStruct{1, 1, 0}
item.add()
fmt.Printf("a={%v},b={%v},c={%v}\n", item.a, item.b, item.c)//a={1},b={1},c={0}
item.p_add()
fmt.Printf("a={%v},b={%v},c={%v}\n", item.a, item.b, item.c)//a={1},b={1},c={2}
}
方法提升
匿名子结构的方法可以像父结构方法一样被使用(没有同名父结构方法)
自定义类型扩展方法
方法并非结构体专有,所有自定义类型都可以定义方法
type myInt int //自定义类型
func (a *myInt) add(b myInt) myInt {
return *a + b
}
num1 := myInt(5)
num2 := myInt(10)
sum := num1.add(num2)