Go基础系列:接口类型断言和type-switch
2018-11-01 23:27
996 查看
接口类型探测:类型断言
接口实例中存储了实现接口的类型实例,类型的实例有两种:值类型实例和指针类型实例。在程序运行过程中,接口实例存储的实例类型可能会动态改变。例如:
// 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它保存的是值类型的实例。
以下是输出结果:
ins1: *main.Square ins2: main.Square
特别需要注意的是,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)。
另一方面,通过接口类型断言(
ins.(Type)),如果Type是一个接口类型,就可以判断接口实例ins中所保存的类型是否也实现了Type接口。例如:
var r io.Read tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } r = tty var w io.Writer w = r.(io.Writer)
上面的r是io.Read接口的一个实例变量,它里面保存的是tty和它的类型,即
(tty, *os.File),然后断言r的类型,探测它里面的类型
*File是否也实现了io.Writer接口,如果实现了,则保存到io.Writer接口的实例变量w中,这样w实例也将保存
(tty,*os.File)。
由于任意内容都实现了空接口,所以,总是可以把一个接口实例无需通过任何断言地赋值给一个空接口实例:
var empty interface{} empty = w
现在empty也保存了
(tty,*os.File)。
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是错误的。
相关文章推荐
- Go基础系列:空接口
- Go基础系列:简单数据类型
- 深入Atlas系列:Web Sevices Access in Atlas示例(5) - 自定义TypeConverter把基础类型转换为复杂类型
- 黑马程序员--Java基础加强--17.利用反射操作泛型VI【泛型类型变量的语义】【GenericDeclaration接口】【泛型接口TypeVariable】【通过Class反射解析泛型类】
- Go基础系列(7):map类型
- golang基础-接口、接口嵌套、类型断言、接口与结构体_接口等转换
- Go基础系列:Go接口
- 黑马程序员--Java基础加强--12.利用反射操作泛型I【与反射+泛型相关的接口类型综述】【Type】【ParameterizedType】【个人总结】
- Go基础系列:数据类型转换(strconv包)
- Go基础系列:Go接口
- Go(6 [接口 类型断言])
- golang基础-接口、接口嵌套、类型断言、接口与结构体_接口等转换
- 深入Atlas系列:Web Sevices Access in Atlas示例(5) - 自定义TypeConverter把基础类型转换为复杂类型
- 深入Atlas系列:Web Sevices Access in Atlas示例(5) - 自定义TypeConverter把基础类型转换为复杂类型
- [C#基础知识系列]专题十:全面解析可空类型 推荐
- EF中的实体类型(EF基础系列篇6)
- java基础(三)------ java中的switch选择结构可以使用那些数据类型的数据作为表达式?
- nl80211_iftype接口类型详解
- java基础:java switch(表达式)中表达式的类型都可以是哪些类型?
- enum枚举类型赋值之switch语句与显示类型转换(Explicit Type Conversion)速度之比较