Golang学习(二十三)多态与类型断言

一、什么是多态

#接口多态分为两种
1、 多态参数      #我们前面定义一个接口并实例化变量之后
                 #这个接口变量就可以接收任何实现了该接口的类型

2、 多态数组     #多态数组和参数的区别在于,他定义了一个数组
                #这个数组的类型就是接口,他用于存其他实现了这个接口的结构体实例化的变量

案例

package main

import "fmt"

type Usb interface {
	Start()
	Stop()
}

type Phone struct{}
func (p Phone) Start(){fmt.Println("手机开始工作")}
func (p Phone) Stop(){fmt.Println("手机停止工作")}

type Camera struct{}
func (c Camera) Start() {fmt.Println("相机开始工作")}
func (c Camera) Stop() {fmt.Println("相机停止工作")}




func main(){
	var usbArr [3]Usb  //这里定义一个Usb接口类型的数组
	fmt.Println(usbArr)

	usbArr[0] = Phone{}  //Phone和Camera结构体已经实现了接口
	usbArr[1] = Phone{}  //将实例化写入到这个接口宿主中使用
	usbArr[2] = Camera{}
	fmt.Println(usbArr)

}

返回

[<nil> <nil> <nil>]   //索引位 0、1、2
[{} {} {}]            //结构体操作 start stop,这里没给动作所以为空

小结

前面我们已知数组是只能存放相同数据类型的值

这里我们将不同的结构体都存放到数组的接口中,这就是多态数组

 

二、类型断言

我们接口定义了一些方法的规范,实现了该接口方法的结构体,我们可以通过接口调用

但是,如果我们在结构体里面添加了接口没定义的方法,那么接口该怎么调用呢

 看一段代码

package main

import "fmt"


type Point struct{
	x int
	y int
}

func main(){
	var a interface{}
	var point Point = Point{1,2}
	a = point
	var b Point
	b = a
	fmt.Println(b)
}

返回

# command-line-arguments
.\main.go:16:4: cannot use a (type interface {}) as type Point in assignment: need type assertion

上面的代码中a = point 将结构体变量赋值给了空接口,我们前面知道空接口是可以接收

任意类型的值的,但是他下面将b = a,又将接口类型的值重新赋值给Point类型是不可用的

不能使用空接口类型进行赋值,如果想要从接口类型转回来需要类型断言操作

 案例

package main

import "fmt"


type Point struct{
	x int
	y int
}

func main(){
	var a interface{}
	var point Point = Point{1,2}
	a = point
	var b Point
	b = a.(Point)   //类型断言
	fmt.Println(b)  //{1 2}
}

1、类型断言是怎么实现的

a.(Point)  类型断言,他会判断a变量是否指向了Point类型的变量

如果是就转换成Point类型,并赋值给b变量,否则报错

(如果想将空接口转换为特定的结构体,就可以使用类型断言)

 错误案例

package main

import "fmt"

func main(){
	var t float32
	var x interface{}
	x = t
	y := x.(float64)  //原先t是float32类型,我们这里故意转换为float64类型
	fmt.Println(y)
}

返回

panic: interface conversion: interface {} is float32, not float64

在进行类型断言时,如果类型不匹配,就会报panic

因此进行类型断言时,要确保原来的空接口指向的就是要断言的类型

我们为了防止panic的出现,这里做一个检测机制

package main

import "fmt"

func main(){
	var t float32
	var x interface{}
	x = t
	y,err := x.(float64)   //在断言时传入的只有两个
                           //一个是具体的数值,一个是状态值err  通过变量接收

	if err {      //判断err 是否为true,判断上面转换是否成功
		fmt.Println("转换成功")
	} else{
		fmt.Println("转换失败")
	}

	fmt.Println("继续执行",y)
   
}

在进行断言时,带上检测机制,如果成功就OK,失败了也不要报panic
panic会使得程序崩溃,后续代码无法执行

另一种写法

package main

import "fmt"

func main(){
	var x interface{}
	var t float32
	x = t

	//直接把类型断言做成判断
	if y,err := x.(float64); err {
		fmt.Println("转换成功")
		fmt.Println(y)
	} else{
		fmt.Println("转换失败")
	}
	fmt.Println("继续执行")
}

类型断言最佳实践1

在前面的Usb接口案例做改进,给Phone结构体添加一个特有的方法call

当Usb接口接收的是Phone结构体变量时,还需要调用call方法

 

package main

import "fmt"

type Usb interface {
	Start()
	Stop()
}

type Phone struct{name string}
func (p Phone) Start(){fmt.Println("手机开始工作")}
func (p Phone) Stop(){fmt.Println("手机停止工作")}


type Camera struct{}
func (c Camera) Start() {fmt.Println("相机开始工作")}
func (c Camera) Stop() {fmt.Println("相机停止工作")}




type Computer struct{}

func (computer Computer) Working(usb Usb) {
	usb.Start()
	usb.Stop()
}

func main(){

	var usbArr [3]Usb
	usbArr[0] = Phone{"vivo"}
	usbArr[1] = Phone{"小米"}
	usbArr[2] = Phone{"尼康"}



	var computer Computer
	for _,value := range usbArr {
		computer.Working(value)
	}
}

我们要做的是,当调用Working方法时,如果发现传入到接口中的是Phone结构体变量时

我们会单独调用一下Phone中特有的Call方法(我们自己创建)

但是我们无法直接去指定usb.Call   因为接口中没有指定这个规范

我们可以依照类型断言去判断接口中的值,是否是Phone类型

如果是则调用Phone下的call方法

package main

import "fmt"

type Usb interface {
	Start()
	Stop()
}

type Phone struct{name string}
func (p Phone) Start(){fmt.Println("手机开始工作")}
func (p Phone) Stop(){fmt.Println("手机停止工作")}

//给Phone添加一个Call方法,这个方法接口是没有的,是Phone特有的方法
func (p Phone) Call(){fmt.Println("手机炸了")} 


type Camera struct{}
func (c Camera) Start() {fmt.Println("相机开始工作")}
func (c Camera) Stop() {fmt.Println("相机停止工作")}

type Computer struct{

}
func (computer Computer) Working(usb Usb) {
	usb.Start()

	if phone,err := usb.(Phone); err == true {   //类型断言,怕判断是否是Phone结构体类型
		phone.Call()
	}

	usb.Stop()
}

func main(){

	var usbArr [3]Usb
	usbArr[0] = Phone{"vivo"}
	usbArr[1] = Phone{"小米"}
	usbArr[2] = Phone{"尼康"}


	//Phone还有一个特有的方法call,请遍历Usb数组,如果是Phone变量
	//除了调用Usb接口声明的方法外,还需要调用Phone特有的方法call
	var computer Computer
	for _,value := range usbArr {
		computer.Working(value)
	}
}

类型断言最佳实践2

写一个函数,循环判断传入参数的类型

 

package main

import "fmt"

func TypeJudge(items... interface{}){
	//items... 可变参数 可以接收多个值放在items中
	//这里是空接口,可以接收任意类型的实参
	for index,x :=  range items{
		switch x.(type) {
		case bool:
			fmt.Printf("第%v个参数是bool类型, 值是%v\n",index,x)
		case float32:
			fmt.Printf("第%v个参数是float32类型, 值是%v\n",index,x)
		case float64:
			fmt.Printf("第%v个参数是float64类型, 值是%v\n",index,x)
		case int,int32,int64:
			fmt.Printf("第%v个参数是整数类型, 值是%v\n",index,x)
		case string:
			fmt.Printf("第%v个参数是string类型, 值是%v\n",index,x)
		default:
			fmt.Printf("第%v个参数是 不确定类型, 值是%v\n",index,x)
		}
	}
}

func main(){
	var n1 float32 = 1.1
	var n2 float64 = 2.2
	var n3 int32 = 30
	var name string = "tom"
	address := "北京"
	n4 := 300

	//上面设置多个字符
	TypeJudge(n1,n2,n3,name,address,n4)
}

返回

第0个参数是float32类型, 值是1.1
第1个参数是float64类型, 值是2.2
第2个参数是整数类型, 值是30
第3个参数是string类型, 值是tom
第4个参数是string类型, 值是北京
第5个参数是整数类型, 值是300

类型断言最佳实践3

在上面的案例的基础上添加了判断是否是结构体

package main

import "fmt"


//添加一个结构体
type Student struct{}


func TypeJudge(items... interface{}){
	for index,x :=  range items{
		switch x.(type) {
		case bool:
			fmt.Printf("第%v个参数是bool类型, 值是%v\n",index,x)
		case float32:
			fmt.Printf("第%v个参数是float32类型, 值是%v\n",index,x)
		case float64:
			fmt.Printf("第%v个参数是float64类型, 值是%v\n",index,x)
		case int,int32,int64:
			fmt.Printf("第%v个参数是整数类型, 值是%v\n",index,x)
		case string:
			fmt.Printf("第%v个参数是string类型, 值是%v\n",index,x)

		   //在case中添加对比Student类型即可
		case Student:
			fmt.Printf("第%v个参数是Student类型, 值是%v\n",index,x)
		case *Student:
			fmt.Printf("第%v个参数是*Student类型, 值是%v\n",index,x)

		default:
			fmt.Printf("第%v个参数是 不确定类型, 值是%v\n",index,x)
		}
	}
}



func main(){
	var n1 float32 = 1.1
	var n2 float64 = 2.2
	var n3 int32 = 30
	var name string = "tom"
	address := "北京"
	n4 := 300

	//声明结构体
	stu1 := Student{}
	stu2 := &Student{}


	//添加stu变量
	TypeJudge(n1,n2,n3,name,address,n4,stu1,stu2)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值