面向对象编程三大特征7
多态及体现:
基本介绍:
变量(实例)具有多种形态。面向对象的第三大特征,在Go语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。
快速入门:
在前面的Usb接口案例, Usb usb,既可以接收手机变量,又可以接收相机变量,就体现了Usb 接口多态特征。
//编写一个方法Working 这个方法接收Usb接口类型的变量
//只要是实现了 Usb接口 (所谓实现Usb接口就是指实现了Usb接口声明的所有方法)
func (c Computer) Working(usb Usb) { //usb 变量会根据传入的实参,来判断到底是Phone,还是Camera,usb 接口变量就体现出多态的特点。
//通过usb接口变量来调用Start和Stop方法
usb.Start()
usb.Stop()
}
接口体现多态特征:
1)多态参数
在前面的Usb接口案例,usb Usb,既可以接收手机变量,又可以接收相机变量,就体现了Usb 接口多态特征。
2)多态数组
演示一个案例:给Usb数组中,存放Phone 结构体 和 Camera 结构体变量,Phone还有一个特有的方法call(),请遍历Usb数组,如果是Phone变量,除了调用Usb 接口声明的方法外,还需要调用Phone 特有方法 call。
蓝色部分讲完类型断言之后再讲特有的调用方法。
案例说明:
//声明一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
}
type Phone struct {
name string
}
//让Phone 实现 Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作...")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作...")
}
type Camera struct {
name string
}
//让camera 实现了Usb接口的方法
func (c Camera) Start() {
fmt.Println("相机开始工作...")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作...")
}
func main() {
//定义一个usb接口数组,可以存放Phone和Camera结构体变量
//这里就体现多态数组
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"三星"}
fmt.Println(usbArr)
}
类型断言:
先看一个需求:
看一段代码
type Point struct {
x int
y int
}
func main() {
var a interface{}
var point Point = Point{1,2}
a = point //ok
//如何将a赋给一个Point变量?
var b Point
b = a //可以吗? ==>error 必须是 b = a.(Point) 类型断言
fmt.Println(b)
}
b = a.(Point) 就是类型断言,表示判断a是否指向Point类型的变量,如果是就转成Point类型并赋给b变量,否则报错。
需求:如何将一个接口变量,赋给自定义类型的变量. => 引出类型断言
基本介绍:
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言,具体的如下:
案例:
func main() {
var x interface {}
var b float32 = 1.1
x = b //空接口,可以接收任意的类型
// x=>float32 [使用类型断言]
y := x.(float32)
fmt.Printf("y 的类型是 %T 值是%v", y, y)
}
对上面的代码说明:
在进行类型断言时,如果类型不匹配,就会报 panic,因此进行类型断言时,要确保原来的空接口指向的就是断言的类型。
比如 y := x.(float32) 写成float64就不行了,因为空接口指向的是float32类型。
如何在进行断言时,带上检测机制,如果成功就OK,否则也不需要报 panic。
带检测的类型断言:
func main() {
var x interface {}
var b float32 = 2.1
x = b //空接口,可以接收任意的类型
// x=>float32 [使用类型断言]
//类型断言(带检测的)
//y, flag := x.(float32)
if y, flag := x.(float32); flag {
fmt.Println("convert success")
fmt.Printf("y 的类型是 %T 值是%v", y, y)
} else {
fmt.Println("convert fail")
}
fmt.Println("继续执行~~~")
}
类型断言的最佳实践1:
在前面的Usb接口案例做改进:
给Phone 结构体增加一个特有的方法call(),当Usb 接口接收的是Phone 变量时,还需要调用call方法。
//声明一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
}
type Phone struct {
name string
}
//让Phone 实现 Usb接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作...")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作...")
}
func (p Phone) Call() {
fmt.Println("手机 在打电话..")
}
type Camera struct {
name string
}
//让camera 实现了Usb接口的方法
func (c Camera) Start() {
fmt.Println("相机开始工作...")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作...")
}
type Computer struct {
}
func (computer Computer) Working(usb Usb) {
usb.Start()
//如果usb是指向Phone结构体变量,则还需要调用Call方法。
//类型断言...[注意体会!!!]
if a, ok := usb.(Phone); ok {
a.Call()
}
usb.Stop()
}
func main() {
//定义一个usb接口数组,可以存放Phone和Camera结构体变量
//这里就体现多态数组
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"三星"}
//遍历usbArr
var computer Computer
for _, v := range usbArr{
computer.Working(v)
fmt.Println()
}
fmt.Println(usbArr)
}
类型断言的最佳实践2:
//编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items... interface{}) { //表示这个函数可以接收多个的任意类型的实参
for i, x := range items {
switch x.(type) { //这里type是一个关键字,固定写法
case bool:
fmt.Printf("第%d个参数是 bool 类型,值是%v \n", i+1, x)
case float32:
fmt.Printf("第%d个参数是 float32 类型,值是%v \n", i+1, x)
case float64:
fmt.Printf("第%d个参数是 float64 类型,值是%v \n", i+1, x)
case int, int32, int64:
fmt.Printf("第%d个参数是 整数 类型,值是%v \n", i+1, x)
case string:
fmt.Printf("第%d个参数是 string 类型,值是%v \n", i+1, x)
default:
fmt.Printf("第%d个参数是 类型 不确定,值是%v \n", i+1, x)
}
}
}
func main() {
var n1 float32 = 1.1
var n2 float64 = 2.3
var n3 int64 = 30
var name string = "tom"
address := "北京"
n4 := 300
TypeJudge(n1, n2, n3, name, address, n4)
}
结果是:
类型断言的最佳实践3:
在前面代码的基础上,增加判断Student 类型和 *Student类型
package main
import (
"fmt"
)
type Student struct {
}
//编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items... interface{}) {
for i, x := range items {
switch x.(type) { //这里type是一个关键字,固定写法
case bool:
fmt.Printf("第%d个参数是 bool 类型,值是%v \n", i+1, x)
case float32:
fmt.Printf("第%d个参数是 float32 类型,值是%v \n", i+1, x)
case float64:
fmt.Printf("第%d个参数是 float64 类型,值是%v \n", i+1, x)
case int, int32, int64:
fmt.Printf("第%d个参数是 整数 类型,值是%v \n", i+1, x)
case string:
fmt.Printf("第%d个参数是 string 类型,值是%v \n", i+1, x)
case Student :
fmt.Printf("第%d个参数是 Student 类型,值是%v \n", i+1, x)
case *Student :
fmt.Printf("第%d个参数是 *Student 类型,值是%v \n", i+1, x)
default:
fmt.Printf("第%d个参数是 类型 不确定,值是%v \n", i+1, x)
}
}
}
func main() {
var n1 float32 = 1.1
var n2 float64 = 2.3
var n3 int64 = 30
var name string = "tom"
address := "北京"
n4 := 300
stu1 := Student{}
stu2 := &Student{}
TypeJudge(n1, n2, n3, name, address, n4, stu1, stu2)
}