接口
接口(多态):仅包含方法定义集合的数据类型(is-a类型)
1)接口是一种抽象类型;
2)实现接口代表:实现该接口类型的所有方法;
空接口:不包含任何方法的接口
1)Go中所有结构体类型均实现空接口;
2)空接口可以存储任意类型的数据(常用于函数参数)
接口类型的定义格式:
type 接口名 interface{
方法1
方法N
其他接口
}
1)接口类型定义中的方法可仅指定方法名(不可指定程序段);
2)接口类型的定义中可包含其他已定义的接口(等同包含其方法);
//定义中方法和其他接口的出现顺序并无意义(也无需指定接收者)
接口变量定义的2种格式:声明后初始化、基于其他接口
(1)仅声明:var 接口变量名 接口类型
1)初始化:接口变量名 = 类型对象
2)若该类型对象的方法有指针接收者,则应使用&传递地址;
3)也可通过匿名结构体类型对象进行初始化(格式:结构体类型{值}
);
4)类型对象含有的方法但接口类型不含有,则接口变量不可调用该方法;
//非指针接收者在被调用时,编译器会隐式解引用
(2)接口变量名 := 其他接口
1)仅当一个接口被实现后,才可使用该接口定义其他接口;
2)被定义的接口可调用其他接口的所有方法(其他接口的别名);
接口变量定义后调用方法的格式:接口变量.方法(参数)
1)本质:通过初始化接口的结构体类型对象调用该方法;
如:定义接口和结构体类型,并实现该接口
1)编写程序;
package main
import (
"fmt"
)
type People interface {
Speak1(string) string
Speak2(string) string
}
type Stduent struct{}
func (stu *Stduent) Speak1(think string) (talk string) {
if think == "love" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func (stu Stduent) Speak2(think string) (talk string) {
if think == "love" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
// var peo1 People = Stduent{} //该格式仅无指针接收者可使用
//由于接口变量(基类)指向类型对象含有指针接收者,需使用&
var peo2 People = &Stduent{}
fmt.Println(peo2.Speak1("hi"), peo2.Speak2("love"))
}
2)运行结果;
值接收者方法和指针接收者方法名不可重复,原理如下:
1)编译器会自动复制值接收者创建相同的指针接收者方法
2)通过接口调用值接收者方法时,本质在调用编译器生成的指针接收者方法
//接口不能直接使用值接收者方法
接口值
接口值:由动态类型(结构体类型)和动态值(结构体类型对象)组成
1)接口零值(nil接口):动态类型和动态值均为nil(与空接口值不同);
2)通过nil接口调用任何方法都会让程序产生宕机;
3)动态类型不可比较,但动态值是可比较的;
//接口变量在未被初始化时,就属于接口零值
Go中所有类型分为两种:静态类型(Static Type)、动态类型(Dynamic Type)
1)静态类型:声明变量时指定的类型(且程序运行期间保持不变);
2)动态类型:由接口变量的值决定该接口变量的结构体类型;
接口值需注意事项:
1)一个接口值可指向任意多个动态值;
2)接口值之间可使用“==
”和“!=
”操作符比较;
如:通过两个实现接口的结构体定义接口变量,并调用方法
1)编写程序;
package main
import (
"fmt"
)
type Animal interface {
Eat()
}
type Cat struct {
name string
}
func (c Cat) Eat() {
fmt.Println(c.name, "Eat fish")
}
type Dog struct {
name string
}
func (d Dog) Eat() {
fmt.Println(d.name, "Eat bone")
}
func main() {
var a1 Animal
a1 = Cat{"mao"}
fmt.Printf("%T\n", a1)
a1.Eat()
a2 := Dog{"Go"}
a1 = a2
fmt.Printf("%T\n", a1)
a1.Eat()
}
2)运行结果;
类型断言
类型断言:判断接口变量的动态类型是否为指定的结构体类型或接口类型
类型断言格式:接口变量.(结构体类型或接口类型)
1)为结构体类型时,若匹配成功则返回该接口变量的动态值;
2)为接口类型时,若匹配成功则返回接口变量(但动态类型变为接口类型);
//匹配失败则返回nil
类型断言的返回值分:一个返回值,两个返回值
1)当仅有一个返回值时:仅返回true/false代表匹配是否成功;
2)当有两个返回值时:第一个返回动态类型,第二个返回true/false;
//第一种匹配失败会导致程序宕机(建议使用第二种)
类型断言方式分为:if语句(Comma-ok断言)、switch语句(类型分支)
(1)if语句,格式:
if 变量1, 变量2 := 接口变量.(结构体类型或接口类型) {
程序段
}
1)变量1一般为空白标识符“_”;
(2)switch语句,格式
switch 接口变量.(type) {
case 结构体类型1:
程序段
case 结构体类型N:
程序段
default:
程序段
}
1)多个结构体类型可共用同一分支,使用“,”分隔;
如:通过类型断言决定程序的执行方式
1)编写程序;
package main
import (
"fmt"
)
type Animal interface {
Eat()
}
type Cat struct {
name string
}
func (c Cat) Eat() {
fmt.Println(c.name, "Eat fish")
}
type Dog struct {
name string
}
func (d Dog) Eat() {
fmt.Println(d.name, "Eat bone")
}
func Eat(a Animal) {
_, ok := a.(Dog)
if ok {
fmt.Print("Dog is Eating,")
}
if _, ok := a.(Cat); ok {
fmt.Print("Cat is Eating,")
}
a.Eat()
}
func main() {
var a1 Animal
a1 = Cat{"mao"}
Eat(a1)
a2 := Dog{"Go"}
a1 = a2
Eat(a1)
}
2)运行结果;