【白话】Go语言的类型断言

类型断言

go里面的类型断言写法:

x.(T)

其中x为interface{}类型,T是要断言的类型
举个栗子:
有一个接口point,有两个变量pointA、pointB,且这两个变量都实现了改接口
假如我们要实现一个函数func display(p point) { … },功能是若传入的参数p的那个变量的动态类型是pointA的话,就输出A,是pointB的话,就输出B
这时候就要用到类型断言

type point interface{}
type A struct{}
type B struct{}

func main() {
	var (
	P point
	a A
	b *B
	)
	P = a
	display(P)
	P = b
	display(P)
}

func display(p point) {
 	if _,ok := p.(A); ok {
		fmt.Println("A")
	}
	 if _,ok := p.(*B); ok {
		fmt.Println("*B")
	}
}

输出:
A
*B

总之,就是判断当前接口变量的动态类型是什么。

断言之前的变量与之后的变量

大家,再看这一个例子:

	var w io.Writer
	w = os.Stdout
	/* var os.Stdout *os.File
	 *os.File为类型
	  os.Stdout为变量
	*/
	f:= w.(*os.File)

定义了一个io.Writer的接口w,然后将os.Stdout赋给了它,因为w是一个io.Writer类型的接口,
所以,即使将os.Stdout赋给了它,w也仅仅暴露w.Write()接口。
断言之后,出现了一个新的变量f,f为os.File类型,而不是一个接口类型的变量,
所以f暴露出了全部
os.File类型可以使用的方法
在这里插入图片描述

断言是否实现了某个接口(包含某个方法的实现)

我们先看一个函数

func writeString(w io.Writer, s string) error{
	if _,err := w.Write([]byte("Content-Type:")); err != nil {
		return err
	}
	if _,err := w.Write([]byte(contentType)); err != nil {
		return err
	}
}

go语言圣经中说,[]byte(“str”),这种行为会发生拷贝,产生内存的分配,造成性能的下降。
我们知道,w变量持有的动态类型中,也有一个允许字符串高效写入的WriteString方法,这个方法可以避免去分配一个临时的内存。
因为我们并不知道,传入这个函数的w参数的动态类型是否实现了这个WriteString方法。
如果我们先去断言一下w持有的动态类型,如果含有WriteString的这个高效的方法,
我们就使用高效的方法,如果没有再使用[]byte(“str”)的方法。
也就是下面的优化

func writeString(w io.Writer, s string) (n int, err error){
	type stringWriter interface{
		WriteString(string) (n int, err error)
	}
	if sw, ok := w.(stringWriter); ok {
		return sw.WriteString(s)
	}
	return w.Write([]byte(s))
}

这是一个很好的技巧
通过这种方式,我们可以断言,某个接口所持有的动态类型是否实现了某个接口(或者说是否实现了哪几个方法)
sw, ok := w.(stringWriter)之后,sw就成了stringWriter接口类型,仅仅暴露出了stringWriter的方法

类型分支

可以看到,我们前面几个断言,都是考虑的方法,判断接口变量所持有的动态类型是什么,或者判断是否含有某个方法。
因为这些场景中,我们并不关心持有动态类型的具体类型是什么
而接下来,我们需要关注是具体类型是什么
来看一个代码

type point interface{}

func quote(x point) {
	switch x.(type){
	case int:
		fmt.Printf("type is int\n")
	case float64:
		fmt.Printf("type is float64\n")
	case string:
		fmt.Printf("type is string\n")
	default:
		fmt.Printf("type is default\n")
	}
}

我们能够知道,可以传入这个函数的变量,肯定都是实现了point接口的,所以他们又有该接口的方法
而此时,我们确实关注他们的具体类型是什么,然后根据具体类型不同再去做相应的事情
但是,这样的话,我们还是没有拿到这个接口所持有的类型变量,所以,仍旧需要进行下面的步骤,
才能把持有的具体变量取出来

temp := x.(int)
fmt.Printf("type is int : %d\n",temp)

所以,有更简单的方法

func quote(x interface{}) {
	switch x := x.(type){
	case int:
		fmt.Printf("type is int:%d\n",x)
	case float64:
		fmt.Printf("type is float64:%f\n",x)
	case string:
		fmt.Printf("type is string:%s\n",x)
	default:
		fmt.Printf("type is default\n")
	}
}

x := x.(type) 虽然已经有了x变量,但是我们仍旧可以再创建一个x并初始化,怎么说呢?Go就是这样规定的
此时的x,就是断言出来的具体的类型的变量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋山刀名鱼丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值