go中的Type Assertion(断言)

go中的接口非常灵活有意思。

定义:

//定义某个接口
type Base interface{
    methodOne()
}
//interface也是一种type,因此也可使用组合的方式来定义
type Super interface{
    Base
    methodTwo()
}
//定义一个类型Vertex
type Vertex struct{}
func (v Vertex) methodOne(){}
func (v Vertex) methodTwo(){}
...

//定义接口变量
var myIFValue0 interface{} //一个空接口的变量
var myIFValue1 Super = Vertex{}  //一个Super的接口变量

go中接口(interface)属于type的一种,它定义了一组方法名的集合。只要某个类型自身实现了该接口里的所有同名方法,我们就说它实现了这个接口。上面代码中Vertex类型实现了接口Super。

而接口变量,顾名思义是类型为interface的变量。分两种:

一种是类型为空接口的变量,如myIFValue0。

一种是既定接口类型的变量,如myIFValue1。

这两种接口变量的区别在于:

*类型为空接口的变量,可以被赋予任何类型的实例。因为空接口所定义的方法数量为0,也就是任何类型的实例都可以被赋予该接口,不需要这些类型实现任何方法。

*类型为既定接口的接口变量,只能被满足该接口的类型赋值。上面的myIFValue1变量,只能被实现了methodOne、methodTwo方法的类型的实例赋值,否则编译器会报错。

可是,接口变量毕竟是个变量,但凡满足接口方法的类型,其实例都可以给该接口变量赋值。

...
var a myIF = TypeOne{} //TypeOne满足接口myIF
...
a = TypeTwo{}          //TypeTwo也满足接口myIF

显然,如果类型TypeOne和TypeTwo对myIF接口的某个同名方法有不同实现,实际上就是所谓多态。此时接口变量a和满足myIF的类型之间,就像一个单刀多郑开关,可按需随时切换接口方法的实现。

赋值:

在go中,接口变量的底层实际可看作是一个tuple:(value, type) 

value指向赋值给该接口变量的type的实例,type是该实例的类型。

接口变量a不仅可以被某个类型的实例赋值,也可被另一个接口变量赋值,前提是另一个接口变量底层的type实现了该接口。

Type Assertion:

go提供了一种方式让我们可以判断某个接口所绑定的类型是否携有某种其他类型:

t, ok := i.(T)

这行代码里,t是类型T的实例,ok是一个布尔值,表示是否携带某种类型。i是接口变量。T是想查询的类型。

这种断言其实很有用。

由于接口也是一种type,所以我们也可以借之判断当前接口是否满足其他接口。

package main
import "fmt"
//定义接口R、W、RW
type R interface{
	Read()
}
type W interface{
	Write()
}
type RW interface{
R
W
}
//定义类型Vertex,实现R接口
type Vertex struct{
}
func (v Vertex) Read(){
	fmt.Println("fun")
}
//func (v Vertex) Write(){
//	fmt.Println("fun")
//}
func main() {
	var a = Vertex{}
	var i R = &a    //不报错,&a实现了R接口


	r, rok := i.(R)
	fmt.Println(r, rok)    //-->&{} true
	w, wok := i.(W)        //&a没有实现W接口,返回nil和布尔值false
	fmt.Println(w, wok)    //--><nil> false
	rw, rwok := i.(RW)     //&a没有实现W接口,返回nil和布尔值false
	fmt.Println(rw, rwok)    //--><nil> false
}

上边代码 ,我们就通过type assertion判断i是否满足R、W、RW接口。

毕竟,我们的接口变量底层所绑定的实例,其类型实现的方法列表,往往是该接口的超集。go提供的这种断言,实际上就是对接口的一种能力检测。

如果把上面第20-22行的注释去掉,则Vertex同时实现了R和W。main函数下边的三个断言的布尔值都返回true。

当然,有时候我们可能需要根据接口的类型做不同动作,在官方的A Tour of Go中提到一种type switch示例:

package main

import "fmt"

type Vertex struct{
} 

func do(i interface{}) {    //形参是一个空接口
	switch v := i.(type) {  //注意type关键字
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}

func main() {
	do(21)
	do("hello")
	do(true)
	do(Vertex{})
}
//输出:
Twice 21 is 42
"hello" is 5 bytes long
I don't know about type bool!
I don't know about type main.Vertex!

猜你喜欢

转载自blog.csdn.net/weixin_36094484/article/details/82195006
今日推荐