Go基础系列:接口类型探测和type-switch
接口类型探测:类型断言
接口实例中存储了实现接口的类型实例,类型的实例有两种:值类型实例和指针类型实例。在程序运行过程中,接口实例存储的实例类型可能会动态改变。例如:
// ins是接口实例 var ins shaper // ins存储值类型的实例 ins = c1 // 一段时间后... ... // ins存储指针类型的实例,存储的类型发生改变 ins = c2 // 一段时间后... // ins可能存储另一个类型实例 ins = s1
所以,需要一种探测接口实例所存储的是值类型还是指针类型。
探测的方法是:ins.(type)
和ins.(*type)
。它们有两个返回值,第二个返回值是ok返回值,布尔类型,第一个返回值是探测出的类型。也可以只有一个返回值:探测出的类型。
// 如果ins保存的是值类型的type,则输出 if t, ok := ins.(type); ok { fmt.printf("%t\n", v) } // 如果ins保存的是指针类型的*type,则输出 if t, ok := ins.(*type); ok { fmt.printf("%t\n", v) } // 一个返回值的探测 t := ins.(type) t := ins.(*type)
以下是一个例子:
package main import "fmt" // shaper 接口类型 type shaper interface { area() float64 } // square struct类型 type square struct { length float64 } // square类型实现shaper中的方法area() func (s square) area() float64 { return s.length * s.length } func main() { var ins1, ins2, shaper // 指针类型的实例 s1 := new(square) s1.length = 3.0 ins1 = s1 if v, ok := ins1.(*square); ok { fmt.printf("ins1: %t\n", v) } // 值类型的实例 s2 := square{4.0} ins2 = s2 if v, ok := ins2.(square); ok { fmt.printf("ins2: %t\n", v) } }
上面两个printf都会输出,因为它们的类型判断都返回true。如果将ins2.(square)
改为ins2.(*square)
,第二个printf将不会输出,因为ins2它保存的是值类型的实例。
特别需要注意的是,ins必须明确是接口实例。例如,以下前两种声明是有效的,第三种推断类型是错误的,因为它可能是接口实例,也可能是类型的实例副本。
var ins shaper // 正确 ins := shaper(s1) // 正确 ins := s1 // 错误
当ins不能确定是接口实例时,用它来进行测试,例如ins.(square)
将会报错:
invalid type assertion:ins.(square) (non-interface type (type of ins) on left)
它说明了左边的ins是非接口类型(non-interface type)。
type switch结构
switch流程控制结构还可以用来探测接口实例保存的类型。这种结构称为type-switch。
用法如下:
switch v := ins.(type) { case *square: fmt.printf("type square %t\n", v) case *circle: fmt.printf("type circle %t\n", v) case nil: fmt.println("nil value: nothing to check?") default: fmt.printf("unexpected type %t", v) }
其中ins.(type)
中的小写type是固定的词语。
以下是一个使用示例:
package main import ( "fmt" ) // shaper 接口类型 type shaper interface { area() float64 } // circle struct类型 type circle struct { radius float64 } // circle类型实现shaper中的方法area() func (c *circle) area() float64 { return 3.14 * c.radius * c.radius } // square struct类型 type square struct { length float64 } // square类型实现shaper中的方法area() func (s square) area() float64 { return s.length * s.length } func main() { s1 := &square{3.3} whichtype(s1) s2 := square{3.4} whichtype(s2) c1 := new(circle) c1.radius = 2.3 whichtype(c1) } func whichtype(n shaper) { switch v := n.(type) { case *square: fmt.printf("type square %t\n", v) case square: fmt.printf("type square %t\n", v) case *circle: fmt.printf("type circle %t\n", v) case nil: fmt.println("nil value: nothing to check?") default: fmt.printf("unexpected type %t", v) } }
上面的type-switch中,之所以没有加上case circle
,是因为circle只实现了指针类型的receiver,根据method set对接口的实现规则,只有指针类型的circle示例才算是实现了接口shaper,所以将值类型的示例case circle
放进type-switch是错误的。
上一篇: python 中文件输入输出及os模块对文件系统的操作方法
下一篇: 开心一刻笑口常开的爆笑动态图