# 2.4 学习接口:详解类型断言
## Type Assertion
Type Assertion(中文名叫:类型断言),通过它可以做到以下几件事情
1. 检查 `i` 是否为 nil
2. 检查 `i` 存储的值是否为某个类型
具体的使用方式有两种:
**第一种:**
```
t := i.(T)
```
这个表达式可以断言一个接口对象(i)里不是 nil,并且接口对象(i)存储的值的类型是 T,如果断言成功,就会返回值给 t,如果断言失败,就会触发 panic。
来写段代码试验一下
```go
package main
import "fmt"
func main() {
var i interface{} = 10
t1 := i.(int)
fmt.Println(t1)
fmt.Println("=====分隔线=====")
t2 := i.(string)
fmt.Println(t2)
}
```
运行后输出如下,可以发现在执行第二次断言的时候失败了,并且触发了 panic
```
10
=====分隔线=====
panic: interface conversion: interface {} is int, not string
goroutine 1 [running]:
main.main()
E:/GoPlayer/src/main.go:12 +0x10e
exit status 2
```
如果要断言的接口值是 nil,那我们来看看也是不是也如预期一样会触发panic
```go
package main
func main() {
var i interface{} // nil
var _ = i.(interface{})
}
```
输出如下,确实是会 触发 panic
```
panic: interface conversion: interface is nil, not interface {}
goroutine 1 [running]:
main.main()
E:/GoPlayer/src/main.go:5 +0x34
exit status 2
```
**第二种**
```
t, ok:= i.(T)
```
和上面一样,这个表达式也是可以断言一个接口对象(i)里不是 nil,并且接口对象(i)存储的值的类型是 T,如果断言成功,就会返回其类型给 t,并且此时 ok 的值 为 true,表示断言成功。
如果接口值的类型,并不是我们所断言的 T,就会断言失败,但和第一种表达式不同的事,这个不会触发 panic,而是将 ok 的值设为 false ,表示断言失败,此时t 为 T 的零值。
稍微修改下上面的例子,如下
```go
package main
import "fmt"
func main() {
var i interface{} = 10
t1, ok := i.(int)
fmt.Printf("%d-%t\n", t1, ok)
fmt.Println("=====分隔线1=====")
t2, ok := i.(string)
fmt.Printf("%s-%t\n", t2, ok)
fmt.Println("=====分隔线2=====")
var k interface{} // nil
t3, ok := k.(interface{})
fmt.Println(t3, "-", ok)
fmt.Println("=====分隔线3=====")
k = 10
t4, ok := k.(interface{})
fmt.Printf("%d-%t\n", t4, ok)
t5, ok := k.(int)
fmt.Printf("%d-%t\n", t5, ok)
}
```
运行后输出如下,可以发现在执行第二次断言的时候,虽然失败了,但并没有触发了 panic。
```
10-true
=====分隔线1=====
-false
=====分隔线2=====
<nil> - false
=====分隔线3=====
10-true
10-true
```
上面这段输出,你要注意的是第二个断言的输出在`-false` 之前并不是有没有输出任何 t2 的值,而是由于断言失败,所以 t2 得到的是 string 的零值也是 `""` ,它是零长度的,所以你看不到其输出。
## Type Switch
如果需要区分多种类型,可以使用 type switch 断言,这个将会比一个一个进行类型断言更简单、直接、高效。
```go
package main
import "fmt"
func findType(i interface{}) {
switch x := i.(type) {
case int:
fmt.Println(x, "is int")
case string:
fmt.Println(x, "is string")
case nil:
fmt.Println(x, "is nil")
default:
fmt.Println(x, "not type matched")
}
}
func main() {
findType(10) // int
findType("hello") // string
var k interface{} // nil
findType(k)
findType(10.23) //float64
}
```
输出如下
```
10 is int
hello is string
<nil> is nil
10.23 not type matched
```
额外说明一下:
- 如果你的值是 nil,那么匹配的是 `case nil`
- 如果你的值在 switch-case 里并没有匹配对应的类型,那么走的是 default 分支
此外,还有两点需要你格外注意
1. 类型断言,仅能对静态类型为空接口(interface{})的对象进行断言,否则会抛出错误,具体内容可以参考:[关于接口的三个"潜规则"](http://golang.iswbm.com/en/latest/c02/c02_07.html)
2. 类型断言完成后,实际上会返回静态类型为你断言的类型的对象,而要清楚原来的静态类型为空接口类型(interface{}),这是 Go 的隐式转换。
## 参考文章
- [Explain Type Assertions in Go](https://stackoverflow.com/questions/38816843/explain-type-assertions-in-go)
- [Go interface 详解 (四) :类型断言](https://sanyuesha.com/2017/12/01/go-interface-4/)
---
- 第一章:基础知识
- 1.1 一文搞定开发环境的搭建
- 1.2 五种变量创建的方法
- 1.3 数据类型:整型与浮点型
- 1.4 数据类型:byte、rune与字符串
- 1.5 数据类型:数组与切片
- 1.6 数据类型:字典与布尔类型
- 1.7 数据类型:指针
- 1.8 流程控制:if-else
- 1.9 流程控制:switch-case
- 1.10 流程控制:for 循环
- 1.11 流程控制:goto 无条件跳转
- 1.12 流程控制:defer 延迟语句
- 1.13 流程控制:理解 select 用法
- 1.14 异常机制:panic 和 recover
- 1.15 语法规则:理解语句块与作用域
- 第二章:面向对象
- 2.1 面向对象:结构体与继承
- 2.2 面向对象:接口与多态
- 2.3 面向对象:结构体里的 Tag 用法
- 2.4 学习接口:详解类型断言
- 2.5 学习接口:Go 语言中的空接口
- 2.6 学习接口:接口的三个"潜规则"
- 2.7 学习反射:反射三定律
- 2.8 学习反射:全面学习反射的函数
- 2.9 详细图解:静态类型与动态类型
- 2.10 关键字:make 和 new 的区别?
- 第三章:项目管理
- 3.1 依赖管理:包导入很重要的 8 个知识点
- 3.2 依赖管理:超详细解读 Go Modules 应用
- 3.3 开源发布:如何开源自己写的包给别人用?
- 3.4 代码规范:Go语言中编码规范
- 第四章:并发编程
- 4.1 学习 Go 函数:理解 Go 里的函数
- 4.2 学习 Go 协程:goroutine
- 4.3 学习 Go 协程:详解信道/通道
- 4.4 学习 Go 协程:WaitGroup
- 4.5 学习 Go 协程:互斥锁和读写锁
- 4.7 学习 Go 协程: 信道死锁经典错误案例
- 4.7 学习 Go 协程:如何实现一个协程池?
- 4.8 理解 Go 语言中的 Context
- 4.9 学习一些常见的并发模型
- 第五章:学标准库
- 5.1 fmt.Printf 方法详解
- 5.2 os/exec 执行命令的五种姿势
- 第六章:开发技能
- 6.1 Go 命令:go test 工具详解
- 6.2 单元测试:如何进行单元测试?
- 6.3 调试技巧:使用 GDB 调试 Go 程序
- 6.4 Go 命令: Go 命令指南
- 第七章:暂未分类
- 7.1 20 个学习 Go 语言的精品网站
