Go基础系列:接口类型探测和type-switch
Posted f-ck-need-u
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
", v)
}
// 如果ins保存的是指针类型的*Type,则输出
if t, ok := ins.(*Type); ok {
fmt.Printf("%T
", 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
", v)
}
// 值类型的实例
s2 := Square{4.0}
ins2 = s2
if v, ok := ins2.(Square); ok {
fmt.Printf("ins2: %T
", 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
", v)
case *Circle:
fmt.Printf("Type Circle %T
", 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
", v)
case Square:
fmt.Printf("Type Square %T
", v)
case *Circle:
fmt.Printf("Type Circle %T
", 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基础系列:接口类型探测和type-switch的主要内容,如果未能解决你的问题,请参考以下文章