golang学习笔记23-面向对象(五):多态与断言【重要】
本节也是GO核心部分,很重要。
注:由于导包语句已经在19讲(笔记19:面向对象的引入)展示过了,所以这里就不展示了。
一、多态(Polymorphism)
变量(实例)具有多种形态,即多态,是面向对象的第三大特征,在Go语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。比如上一讲的greet的参数:s SayHello就是多态参数,这个参数有中国人和美国人两种形态。那么在此之上,我们还可以定义多态数组,来存放不同形态的。示例,首先在之前定义好的Chinese和American结构体中添加名称字段,即Name string,然后在main中测试:
func main() {
var arr [3]utils.SayHello
arr[0] = utils.Chinese{"李华"}
arr[1] = utils.American{"Lily"}
arr[2] = utils.Chinese{"赵六"}
fmt.Println(arr)
}
注意:类似arr[0].Name是错的,因为接口并不暴露具体实现类型的字段。要访问 Name字段,可以创建一个方法来返回名称。比如在 Chinese 和 American 结构体中定义一个 GetName() 方法,但这种方法显然太繁了,其实GO可以直接将接口类型转换为具体的类型,这就是接下来要说的断言。
二、断言(Assertion)
【1】什么是断言?
Go语言里面有一个语法,可以直接判断是否是该类型的变量:var x,ok=element.(T)
,这里value就是变量的值,ok是一个bool类
型,element是interface变量,T是断言的类型。
【2】断言的案例引入:在utils中定义中国人结构体的新方法:
func (c Chinese) NiuYangGe() {
fmt.Println("东北人,扭秧歌")
}
此时如果在Greet函数中添加s.NiuYangGe()就会报错:type utils.SayHello has no field or method NiuYangGe
。意思是接口中没有NiuYangGe()这个方法,但这其实不是接口声明的问题,是编译器不知道传入的这个接口变量是哪个类型的,即Chinese的还是American的,所以要把它改为断言语句,所以在Greet函数的代码如下:
func Greet(s SayHello) {
s.Say()
c, flag := s.(Chinese)
if flag {
c.NiuYangGe()
} else {
fmt.Println("美国人没有扭秧歌")
}
}
当然,如果知道s能转为Chinese,即Chinese有NiuYangGe()这个方法,也可以简写:
c2 := s.(Chinese)
c2.NiuYangGe()
那么一般来说,还是采用第一种方法,这样严谨。
这样一来,我们在main中就可以正确调用新方法了:
func main() {
var c utils.SayHello = utils.Chinese{}
utils.Greet(c)
}
【3】Type Switch的基本用法Type Switch是Go语言中一种特殊的switch语句,它比较的是类型而不是具体的值。它判断某个接口变量的类型,然后根据具体类型再做相应处理。首先在utils中,增加新方法:
func (a American) Disco() {
fmt.Println("Disco")
}
然后在Greet()中判断,代替第二点的判断语句:
func Greet(s SayHello) {
s.Say()
// c, flag := s.(Chinese)
// if flag {
// c.NiuYangGe()
// } else {
// fmt.Println("美国人没有扭秧歌")
// }
// c2 := s.(Chinese)
// c2.NiuYangGe()
switch person := s.(type) {
case Chinese:
person.NiuYangGe()
case American:
person.Disco()
default:
fmt.Println("未知类型")
}
}
最后在main中调用:
func main() {
var c utils.SayHello = utils.Chinese{}
utils.Greet(c)
c = utils.American{}
utils.Greet(c)
}
程序输出如下:
你好
东北人,扭秧歌
hello
Disco