类型断言
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,就是断言出来的具体的类型的变量