如果你想学到更多实用知识。
可以关注我的公众号:【前端驿站Lite】,一个不止分享前端的地方 ᕦ( •̀∀•́)ᕤ
什么是断言
因为多态,一个接口可以被多个类型实现,在使用接口类型变量时,有时候需要确定这个变量具体的类型,这时就需要使用类型断言了。
在Go语言中,断言(Assertion)是一种用于判断接口类型的机制,将接口变量转换成另外一个接口或者另外一个类型,它允许我们在程序中判断一个接口的实际类型是否与我们预期的类型相同,以便进行相应的处理。
所以类型断言是由于接口不知道具体类型,如果要转成具体类型,就需要使用类型断言。
断言的语法形式为x.(T)
,其中x
是一个接口类型的值
,T
是一个具体的类型
。这个语法表示我们要判断x
的实际类型是否是T
,类型断言会返回两个值,一个是实际转换后的值,另一个是指示转换是否成功的布尔值。
value, ok := interfaceValue.(interfaceType)
特别注意:断言只能用于接口类型
基本操作
// 基本格式
x.(T)
v := x.(T)
v, ok := x.(T)
类型断言的必要条件是x
是接口类型,非接口类型的x不能做类型断言
var i int = 10
v := i.(int) //错误例子
T
可以是非接口类型
,如果想断言合法,则T
应该实现x
的接口。
T
也可以是接口,则x
的动态类型也应该实现接口T
。
var x interface{} = 7 // x 的动态类型为int, 值为 7
i := x.(int) // i 的类型为 int, 值为 7
type I interface { m() }
var y I // y 为 I 接口类型变量
s := y.(string) // 非法: string 没有实现接口 I (missing method m)
r := y.(io.Reader) // y 如果实现了接口io.Reader和I的情况下, r的类型则为io.Reader
简单的例子: 如果类型 A 和类型 B 都实现了接口 I,那么你可以通过类型断言将类型 A 转换为类型 B。但如果类型 C 并不实现接口 I,那么将类型 A 转换为类型 C 是不合法的,会导致编译错误或运行时错误
类型选择
在 Go 语言中,类型选择(Type Switch)是一种多分支的语句,用于根据接口值的类型进行不同的处理。类型选择的语法如下:
switch interfaceValue.(type) {
case type1:
// 处理 type1 类型
case type2:
// 处理 type2 类型
...
default:
// 处理其他类型
}
其中,interfaceValue
表示接口值,type1
表示第一个类型,type2
表示第二个类型,default
表示其他类型。在每个分支中,我们可以根据类型进行不同的处理。
接口转换其他类型
如果自身是一个指针类型,那么被判断的类型也应该是指针类型。
func main() {
// 创建结构体指针类型
p1 := new(Hero)
// Thanos := new(Demon)
var fighter Fighter = p1
// Fighter 接口 转换为 *Hero
p2 := fighter.(*Hero)
fmt.Printf("p1=%p\n", p1)
fmt.Printf("p2=%p\n", p2)
}
实践
基本使用
比如:我们可以使用类型断言将一个 interface{}
类型转换为 int
类型:
func main() {
var i interface{} = 10 // 空接口可以接收任意数据类型
if v, ok := i.(int); ok {
fmt.Printf("i is a int, value is %d\n", v)
} else {
fmt.Println("i is not a int")
}
}
在上面的代码中,我们定义了一个 interface{}
变量 i
,并将其赋值为 10
。然后,我们使用类型断言将 i
转换为 int
类型,并判断类型转换是否成功。如果类型转换成功,我们就可以使用转换后的值 v
。
类型选择
类型判断,写一个函数来循环判断传入的参数类型。
package main
import "fmt"
func JudgeType(items ...interface{}) {
for index, value := range items {
switch value.(type) {
case bool:
fmt.Printf("第%v个参数时bool类型,值为%v\n", index, value)
case float32:
fmt.Printf("第%v个参数时float32类型,值为%v\n", index, value)
case float64:
fmt.Printf("第%v个参数时float64类型,值为%v\n", index, value)
case int:
fmt.Printf("第%v个参数时int类型,值为%v\n", index, value)
case string:
fmt.Printf("第%v个参数时string类型,值为%v\n", index, value)
default:
fmt.Printf("第%v个参数类型不确定,值为%v\n", index, value)
}
}
}
func main() {
var a int = 10
var b string = "happy"
var c float32 = 3.21
JudgeType(a, b, c)
}
进阶用法
package main
import "fmt"
type Animal interface {
Sound()
}
type Dog struct{}
func (d Dog) Sound() {
fmt.Println("Woof!")
}
type Cat struct{}
func (c Cat) Sound() {
fmt.Println("Meow!")
}
func main() {
animals := []Animal{Dog{}, Cat{}}
for _, animal := range animals {
// 判断接口类型是否是Dog
if dog, ok := animal.(Dog); ok {
dog.Sound()
}
// 判断接口类型是否是Cat
if cat, ok := animal.(Cat); ok {
cat.Sound()
}
}
}
在上面的代码中,我们定义了一个Animal
接口,以及两个实现了Animal
接口的类型Dog
和Cat
。在main
函数中,我们创建了一个包含Dog
和Cat
实例的切片animals
。然后,我们使用for range
循环遍历切片中的每一个元素。
在循环体中,我们使用断言来判断当前元素的实际类型,并根据类型执行相应的操作。如果当前元素的实际类型是Dog
,则将其转换为Dog
类型并调用Sound
方法;如果当前元素的实际类型是Cat
,则将其转换为Cat
类型并调用Sound
方法。
总结:
断言是 Go 语言中非常重要的特性,可以帮助我们将接口类型
转换为其他类型
,并根据接口值的类型进行不同的处理。掌握断言的使用方法可以提高代码的灵活性和可维护性。