在Golang中,空接口 interface{}
没有定义任何函数,所以Golang 中全部类型都实现了空接口。当一个函数的形参是interface{}
,那么在函数中,须要对形参进行断言,从而获得它的真实类型。函数
在学习接口断言以前,先了解一下类型断言,其实接口断言也是在判断类型。学习
类型断言,经过它能够作到如下几件事情:测试
nil
某个类型
一般有两种方式:
第一种:url
t := i.(T)
复制代码
这个表达式能够断言一个接口对象i
里不是 nil
,而且接口对象i
存储的值的类型是 T
,若是断言成功,就会返回值给t
,若是断言失败,就会触发 panic
。spa
t := i.(T)
经常使用于 switch
结构。指针
第二种:code
t, ok:= i.(T)
复制代码
这个表达式也是能够断言一个接口对象t
里不是 nil
,而且接口对象t
存储的值的类型是 T
;对象
若是断言成功,就会返回其类型给t
,而且此时 ok
的值 为 true
,表示断言成功。
若是接口值的类型,并非咱们所断言的 T
,就会断言失败,但和第一种表达式不一样的事,这个不会触发 panic
,而是将 ok 的值设为 false
,表示断言失败,此时t
为 T
的零值。
t, ok:= i.(T)
经常使用于 if else
结构。
t, ok := i.(T)
断言在上一小节已经介绍过了,本小节,咱们经过实战加深下理解。
咱们先建立一个Shape
形状接口,两个结构体。
// 定义接口
type Shape interface {
perimeter() float64 // 返回形状的周长
area() float64 // 返回形状的面积
}
// 定义结构体 type Circle struct { radius float64 } type Triangle struct { a, b, c float64 } 复制代码
其中,Shape
接口有两个方法,分别是求形状的周长和面积。
两个结构体分别定义了本身独有的属性:
Circle
(圆),定义了半径
Triangle
(三角形),定义了三条边
接下来,咱们实现Shape
接口中的方法:
// 圆结构体 实现接口方法
func (c Circle) perimeter() float64 {
return c.radius * math.Pi * 2
}
func (c Circle) area() float64 { return math.Pow(c.radius, 2) * math.Pi } // 三角形结构体 实现接口方法 func (t Triangle) perimeter() float64 { return t.a + t.b + t.c } func (t Triangle) area() float64 { p := t.perimeter() / 2 return math.Sqrt(p * (p - t.a) * (p - t.b) * (p - t.c)) } 复制代码
其中三角形的面积计算使用了 海伦公式
接下来咱们封装一个接口断言函数:
// 定义接口断言函数
func getInterfaceType(s Shape) {
if ins, ok := s.(Triangle); ok {
fmt.Println("是三角形,三边分别为:", ins.a, ins.b, ins.c)
} else if ins, ok := s.(Circle); ok {
fmt.Println("是圆形,半径为;", ins.radius)
} else if ins, ok := s.(*Circle); ok {
fmt.Printf("是圆形结构体指针,类型为:%T,存储的地址为:%p,指针自身的地址为:%p\n", ins, &ins, ins)
} else {
fmt.Println("没法判断类型...")
}
}
复制代码
该函数中不只判断了值传递的类型,也判断了引用传递(指针类型)的类型。由于Struct
是值类型,因此咱们加入引用类型,使练习更严谨一点。
接下来开始初始化结构体:
// 初始化一个圆结构体
c1 := Circle{radius: 10}
fmt.Println("==================圆结构体:==================")
fmt.Println("圆的周长为:", c1.perimeter())
fmt.Println("圆的面积为:", c1.area())
// 初始化一个三角形结构体 t1 := Triangle{ a: 3, b: 4, c: 5, } fmt.Println("================三角形结构体:=================") fmt.Println("三角形的周长为:", t1.perimeter()) fmt.Println("三角形的面积为:", t1.area()) // 初始化一个圆形结构体指针 var c2 *Circle = &Circle{radius: 5} fmt.Println("================圆形结构体指针:===============") fmt.Println("圆的周长为:", c2.perimeter()) fmt.Println("圆的面积为:", c2.area()) 复制代码
输出:
==================圆结构体:==================
圆的周长为: 62.83185307179586
圆的面积为: 314.1592653589793
================三角形结构体:=================
三角形的周长为: 12
三角形的面积为: 6
================圆形结构体指针:===============
圆的周长为: 31.41592653589793
圆的面积为: 78.53981633974483
复制代码
能够看到,以上结构体都实现了Shape
接口, 接下来开始进行接口断言:
fmt.Println("==============t, ok:= i.(T) 开始接口断言====================")
getInterfaceType(c1) // 判断该接口是否为 圆形结构体类型
getInterfaceType(t1) // 判断该接口是否为 圆形结构体类型
getInterfaceType(c2) // 判断该接口是否为 圆形结构体指针类型
复制代码
输出:
==============t, ok:= i.(T) 开始接口断言===================
是圆形,半径为; 10
是三角形,三边分别为: 3 4 5
是圆形结构体指针,类型为:*main.Circle,存储的地址为:0xc000006030,指针自身的地
址为:0xc0000140e0
复制代码
能够看到,咱们的接口断言奏效了,而且输出了对应逻辑的结果。
断言其实还有另外一种形式,就是用在利用switch
语句判断接口的类型。
每个case
会被顺序地考虑。当命中一个case
时,就会执行 case
中的语句。
所以 case
语句的顺序是很重要的,由于颇有可能会有多个 case
匹配的状况。
咱们再封装一个 switch
逻辑的接口断言函数,逻辑和以前的如出一辙,只是条件语句换成了 switch....case
:
// 定义接口断言函数,使用 switch
func getInterfaceTypeSwitch(s Shape) {
switch ins := s.(type) { // 首字母小写的 type
case Circle:
fmt.Println("是圆形,半径为;", ins.radius)
case Triangle:
fmt.Println("是三角形,三边分别为:", ins.a, ins.b, ins.c)
case *Circle:
fmt.Printf("是圆形结构体指针,类型为:%T,存储的地址为:%p,指针自身的地址为:%p\n", ins, &ins, ins)
default:
fmt.Println("没法判断类型...")
}
}
复制代码
接下来测试封装的函数:
fmt.Println("==============t := i.(type) 开始接口断言====================")
getInterfaceTypeSwitch(c1) // 判断该接口是否为 圆形结构体类型
getInterfaceTypeSwitch(t1) // 判断该接口是否为 圆形结构体类型
getInterfaceTypeSwitch(c2) // 判断该接口是否为 圆形结构体指针类型
复制代码
输出:
==============t := i.(type) 开始接口断言====================
是圆形,半径为; 10
是三角形,三边分别为: 3 4 5
是圆形结构体指针,类型为:*main.Circle,存储的地址为:0xc000006038,指针自身的地
址为:0xc0000140e0
复制代码
能够看到,switch
断言的逻辑也正常输出了。
总结一下,今天主要记录了接口如何断言的,一般有两种方式:
t, ok:= i.(T)
t
,而且此时
ok
的值 为
true
,表示断言成功
ok
为
false
,
t
为
T
的零值
if else
结构
t := i.(T)
i
里不是
nil
,而且接口对象
i
存储的值的类型是
T
t
,若是断言失败,就会触发
panic
switch
结构
本文使用 mdnice 排版