您现在的位置是:首页 >学无止境 >go破冰之旅·15·go语言interface之综合玩法网站首页学无止境
go破冰之旅·15·go语言interface之综合玩法
相信一次5-10分钟即可搞定,实用+效率!
上文中,我们重点看了go语言的interface、方法及其接收者的各种玩法:go接口、方法及接收者重点说明
本文我们重点变换Interface看看接口的其它玩法及注意事项。
目录
接口类型作为参数、接口类型的变量
接口类型也可以作为参数进行传递,传递过程中可以自然而然的使用其方法,这里贴一下基本定义:
// 定义一个动物的基准接口,定义一个“吃”的行为
type IAnimals interface {
Eat()
}
// 对于本案例来说要不要Animals 结构体都可
// 为了一起把“组合”模式结合进来,这样看起来更丰富
type Animals struct {
Name string
}
// Cat将Animals组合了进来
type Cat struct {
Animals
}
type Bird struct {
Animals
}
func (a Cat) Eat() {
fmt.Printf("I'm %s, eating a fish.
", a.Name)
}
func (a Bird) Eat() {
fmt.Printf("I'm %s, eating a insect.
", a.Name)
}
单独再给出一个函数,将接口类型作为参数传入:
// 用i来“吃”,调用之前不知道谁在“吃”
func Eating(i IAnimals) {
i.Eat()
}
定义一个接口类型的变量(准备好函数参数):
var animal IAnimals
// 接口类型的变量可以存储任何实现了该接口的类型的值,这里为Cat
animal = Cat{Animals{Name: "cat_john"}}
// cat_john在吃
Eating(animal) // I'm cat_john, eating a fish.
结果在注释中,可以看到这样也可以玩,但万变不离其宗,核心主线还是实例化的对象能调用它实现的接口的方法。
实现多个接口
对于上述这个典型的案例,鸟除过吃还可以有自己独特的要素,那就是“飞”;
猫除过吃还有自己的专属“喵喵叫”,我们来实现下:
// Cat独有的要素/行为接口
type ICatCall interface {
Meow() // 喵喵叫
}
// Bird独有的要素/行为接口
type IBirdFly interface {
Fly() // 飞
}
// Bird除过实现Eat()方法,还实现了Fly()
func (a Bird) Fly() {
fmt.Printf("I'm %s, i can fly.
", a.Name)
}
// Cat实现了Meow()
func (a Cat) Meow() {
fmt.Printf("I'm %s, i can miaomiaojiao.
", a.Name)
}
调用各自特有方法,一块贴出结果:
var animalCat = new(Cat)
animalCat.Name = "cat_john"
animalCat.Meow()
animalCat.Eat()
var animalBird = new(Bird)
animalBird.Name = "bird_frank"
animalBird.Eat()
animalBird.Fly()
/*
I'm cat_john, i can miaomiaojiao.
I'm cat_john, eating a fish.
I'm bird_frank, eating a insect.
I'm bird_frank, i can fly.
*/
可以看到不论是Cat、Bird都实现了两个接口,其中有一个定义的是各自独有的行为,你还可以基于此继续扩展。
接口嵌套与综合应用
我们再考虑这种情况,鸟除过飞之外,还可以游泳,对于鸟来说我们将飞、游泳分开设计了接口,但这些都属于鸟自有的运动行为,能不能综合起来呢?
我们增加一个游泳接口,并将已有的飞接口和游泳接口合并起来,形成一个鸟的运动接口:
// Bird独有的要素/行为接口
type IBirdSwim interface {
Swim() // 游泳
}
// swim
type IBirdSport interface {
IBirdFly
IBirdSwim
}
// 让Bird也实现IBirdSwim接口
func (a Bird) Swim() {
fmt.Printf("I'm %s, i can swim.
", a.Name)
}
来让Bird飞舞一下:
// 先这么玩,实例化一个Bird对象,并依次调用各个方法
var animalBird = new(Bird)
animalBird.Name = "bird_frank"
animalBird.Eat()
animalBird.Fly()
animalBird.Swim()
// 再这么玩,声明一个IBirdSport接口变量
var animal IBirdSport
// 实例化为Bird
animal = Bird{Animals{Name: "bird_frank"}}
animal.Fly()
animal.Swim()
speed running:
I'm bird_frank, eating a insect.
I'm bird_frank, i can fly.
I'm bird_frank, i can swim.
I'm bird_frank, i can fly.
I'm bird_frank, i can swim.
没什么问题,但第二个玩法中怎么调用不了Eat()这个基本行为了呢?
animal变量和第一种中的animalBird不同,第一种是Bird类型的对象,它实现了IAnimals、IBirdFly、IBirdSwim三个接口,因此它可以调用这三个方法;
而在第二种调用中,只能调用Fly()和Swim()方法,因为IBirdSport仅嵌套了IBirdFly、IBirdSwim两个接口,如果也要保证能Eat(),则将IAnimals 这个基本接口也嵌入IBirdSport中就可以了。
接口类型的变量.方法
在上述的各种案例中,不止一次的出现了这种情况:
// 用i来“吃”,调用之前不知道谁在“吃”
func Eating(i IAnimals) {
i.Eat()
}
再进行简单的变换:
var i IAnimals
// other opts
i.Eat()
也就是使用接口变量直接调用了方法,当出现这种情况时需满足两个条件,否则接口变量无法完成调用其方法:
1,变量 i 一定不为nil,否则就是空指针调用;
2,一定是已经基于实现该接口的类型实例化后的对象赋值给了i,这时i就具有了实际意义,并非仅使用IAnimals进行了声明。