Go通关06:struct和interface,结构体和接口的使用
结构体
定义
结构体是种聚合类型,里面可以包含任意类型的值,这些值就是结构体的成员,或成为字段,定义结构体,需要使用 type+struct 关键字组合
type person struct { //人结构体
name string //人的名字
age uint //人的年龄
}
- type 与 struct 是关键字,用来定义一个新结构体的类型。
- person 为结构体名字。
- name/age 为结构体的字段名,后面指对应的字段类型。
- 字段声明和变量类似,变量名在前,类型在后
- 字段可以是人一个,一个字段都没有的结构体,成为空结构体。
- 结构体也是一种类型,比如 person 结构体和 person 类型是一个意思。
声明
- 像普通字符串、整型医院声明初始化
var p person
声明了一个person类型的变量p,但是没有初始化,所以默认使用结构体里字段的零值。
- 字面量方式初始化
p := person{"无尘",18}
表示结构体变量 p 的name字段初始化为“无尘”,age字段初始化为18。顺序必须和字段定义顺序一致。
- 根据字段名称初始化
p := person{age:18,name:"无尘"}
像这样指出字段名,就可以打乱初始化字段的顺序。也可以只初始化其中部分字段,剩余字段默认使用零值:
p := person{age:30}
字段结构体
结构体字段可以是任意类型,包括自定义的结构体类型:
type person struct { //人结构体
name string
age uint
addr address //使用自定义结构体类型
}
type address struct { //地址结构体
city string
}
对于这样嵌套结构体,初始化和一般结构体类似,根据字段对应的类型初始化即可:
p := person {
age:18,
name:"无尘",
addr:address{
city:"北京",
},
}
结构体的字段和调用一个类型的方法一样,都是使用点操作符“.”:
fmt.Println(p.age)
//访问嵌套结构体里的city字段的值:
fmt.Println(p.addr.city)
接口
定义
接口是一个抽象的类型,是和调用方的一种约定。接口只需要定义约定,告诉掉用方可以做什么,而不用知道它的内部实现。
接口的定义是 type + interface关键字类实现。
//Info 是一个接口,它有方法 Getinfo()string
type Info interface {
Getinfo() string
}
对应 Stringer 接口,它会告诉调用者可以通过 String()放获取一个字符串,这就是接口的约定,而这个字符串是怎么获取到的,接口并不关心,调用者也不用关心,因为这些是接口的实现者来处理的。
接口的实现
接口的实现者必须是一个具体的类型:
func (p person) Getinfo() string {
return fmt.Sprintf("my name is %s,age is %d",p.name,p.age)
}
- 给结构体类型 person 定义了一个方法,这个方法和接口里的方法名称、参数、返回值都一样,就表示这个结构体 person 实现了 Info 接口。
- 如果一个接口有多个方法,那么要实现接口中的所有方法才算是实现了这个接口。
使用
我们先定义一个可以打印 Info 接口的函数:
func printInfo(i Info) {
fmt.Println(i.Getinfo())
}
- 定义函数 pringInfo,它接收一个 Info 接口类型的参数,然后打印接口 Getinfo 方法返回的字符串。
- 这个 pringInfo 函数此处是面向接口编程,只有任何一个类型实现了Info接口,都可以使用这个函数打印出对应的字符串,而不用关心具体的类型实现。
printInfo(p)
//结果为:my name is 无尘,age is 18
因为 person 类型实现了Info接口,所以变量p可以作为函数printInfo的参数。
值接受者、指针接受者
- 实现一个接口,必须实现接口中所有的方法。
- 定义一个方法,有值类型接收者和指针类型接收者,两者都可以调用方法,因为Go编译器自动做了转换。
- 但是接口的实现,值类型接收者和指针类型接收者不一样
上面接口体person实现了Info接口,是否结构体指针也实现了该接口呢?
printInfo(&p)
测试发现p的指针作为参数函数也是可以正常运行,表明以值类型接收者实现接口,类型本身和该类型的指针类型,都实现了该接口
那么把接收者改成指针类型:
func (p *person) Getinfo() string {
return fmt.Sprintf("my name is %s,age is %d",p.name,p.age)
}
然后再调用函数 printInfo(p)
,代码编译不通过,表明以指针类型接收者实现接口,只有对应的指针类型才被认为实现了接口
方法接收者 | 实现接口类型 |
---|---|
(p person) | person和*person |
(p *person) | *person |
- 当值类型作为接收者,person类型和*person类型都实现了该接口。
- 当指针类型作为接收者,只有 *person类型实现了该接口。