7.结构体和方法
自定义类型
在go语言中可以语句基本类型定义一个全新的类型
// 使用type关键字,基于int类型定义一个全新的类型MyInt
type MyInt int
类型别名
给已有的类型起一个别的名字
// 给int类型起一个别名:NewInt
type NewInt = int
结构体
一种可以包含多种其他数据类型的数据类型
定义
使用type
和struct
来定义结构体:
type 类型名 struct{
字段名 字段类型
字段名 字段类型
...
}
- 类型名:在同一个包内不能重复
- 字段名:在同一个结构体中不能重复
- 字段类型:表示对用字段的具体类型
type person struct{
name string
age int
city string
}
// 字段类型相同的可以写在同一行
// 在定义时可以将类型相同的定义在一起,节省内存
type person struct{
name, city string
age int
}
结构体实例化
type person struct{
name, city string
age int
}
// 和声明普通变量一样
// var 变量名称 变量类型
var liming person
liming.name="李明"
liming.age=18
liming.city="北京"
fmt.Println(liming.name)
fmt.Println(liming.age)
fmt.Println(liming.city)
匿名结构体
func main(){
var lisi struct{
name string
age int
}
lisi.name = "李四"
lisi.age=20
}
结构体指针
type person struct{
name, city string
age int
}
func main(){
var han = new(person)
(*han).name = "韩梅梅"
(*han).age = 21
(*han).city = "上海"
// 在go语言中提供了语法糖,对于指针类型的结构体,可以不取值,直接操作
han.name = "韩梅梅2"
han.age = 22
han.city = "浦东"
}
取结构体的地址进行实例化
type person struct{
name, city string
age int
}
func main(){
p:=&person{}
fmt.Printf("%T\n", p3) // *person
fmt.Printf("%v\n", p3) //&{name:"",city:"",age:0}
p.name="人"
}
结构体的初始化
type person struct{
name, city string
age int
}
func main(){
// 1.键值对初始化
p1:=person{
name:"小王",
city:"北京"
// age:19 // 可以少字段,没有赋值的字段就是对应字段类型的零值
}
// 2.值的列表进行初始化(不推荐),要与定义结构体时的顺序一一对应,且不可以少字段
p2:=person{
"老王",
"北京"
"12"
}
}
结构体构造函数
其实就是写一个函数来构造结构体实例
因为结构体是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以一般构造函数返回的是结构体指针类型
func newPerson(name,city string, age int)*person{
return &person{
name:name
city:city
age:age
}
}
func main(){
p := newPerson("王子", "北京", 18)
}
方法和接收者
方法是一种特定类型变量的函数,这种特定类型变量叫做接收者
。接收者的概念类似于其他语言的this
或self
func (接收者变量名称 接收者类型) 方法名 (参数列表)(返回参数){
函数体
}
- 接收者变量名称:官方建议是有接收者类型名的第一个小写字母,例如:Person类型的接收者变量应命名为P。
- 接收者类型:和参数类型类似,可以是指针类型和非指针类型
- 方法名、参数列表、返回参数:于函数定义时相同
例:
// Person 是一个结构体
type Person struct{
name string
age int
}
// NewPerson 是一个Person类型的构造函数
func NewPerson(name string, age int)*Person{
return &Person{
name:name,
age:age,
}
}
// Dream 是为Person类型定义的方法
func (p Person)Dream(){
fmt.Pringf("%s的梦想是学好go语言", p.name)
}
// SetAge 是一个修改年龄的方法,指针接收者指的是接收者的类型是指针
func (p *Person)SetAge(newAge int){
p.age = newAge
}
// SetAge2是一个使用值接收者来修改年龄的方法
func (p Person) SetAge2(newAge int){
p.age = newAge
}
func main(){
p1:=NewPerson("小李", 19)
p1.Dream()
p.SetAge()// 使用指针接收者修改后,可以修改接收者的值
p.SetAge2()// 这里修改后,p.age不会变化,因为是值拷贝
}
什么时候应该使用指针类型
- 需要修改接收者的中的值
- 接收者是拷贝代价比较大的大对象
- 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接受者