Type assertions

类型断言并不真正将接口转换为另一种数据类型,但它提供了对接口具体值的访问,这通常是你想要的。

类型断言提供对接口值的基础具体值的访问。

t := i.(T)

该语句断言接口值 i 保持具体类型 T 并将基础 T 值分配给变量 t。

如果 i 不能持有 T,这个语句将发生 panic。

要测试接口值是否包含特定类型,类型断言可以返回两个值:基础值和报告断言是否成功的布尔值。

t, ok := i.(T)

如果 i 能持有 T,t 将得到基础值,ok 将是 true。

如果不能, ok 将是 false,t 将是 T 类型的零值, 并且没有 panic 发生。

请注意此语法与从 map 读取的语法之间的相似性。

Tour Examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
	var i interface{} = "hello"

	s := i.(string)
	fmt.Println(s)

	// ok 是 true 表明 i 存储的是 string 类型的值,false 则不是。
	s, ok := i.(string)
	fmt.Println(s, ok)

	// ok 是 true 表明 i 存储的是 float64 类型的值,false 则不是。
	f, ok := i.(float64)
	fmt.Println(f, ok)

	f = i.(float64) // panic
	fmt.Println(f)
}

Type Switch

type switch 串行执行多个类型断言,并以匹配类型运行第一个匹配。

用途:如果需要区分多种类型,可以使用 type switch 断言,这个将会更简单更直接。

1
2
3
4
5
6
switch t := i.(type) {
case *S:
    fmt.Println("i store *S", t)
case *R:
    fmt.Println("i store *R", t)
}

示例分析

为什么示例 1 报错,而示例 2 可以正常运行?

#示例 1:

1
2
3
4
5
6
7
var idx interface {}
var idx2 int
switch v := idx.(type) {
    case int, int8, int16, int32, int64:
    case float32, float64:// cannot convert v (type interface {}) to type int: need type assertion
        idx2 = int(v)
}

#示例 2:

1
2
3
4
5
6
7
8
var idx interface {}
var idx2 int
switch v := idx.(type) {
    case int, int8, int16, int32, int64:
    case float32:
    case float64:
        idx2 = int(v)
}

详细解释:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"
import "reflect"

func foo(idx interface{}) {
    switch v := idx.(type) {
    case int, int32:
        fmt.Println(reflect.TypeOf(&v))
    case float64:
        fmt.Println(reflect.TypeOf(&v))
    }
}
func main() {
    foo(1)
    foo(1.)
}
// Result:
// *interface {}
// *float64

对于语句:

1
switch v := idx.(type)

在 case 语句里面 v 的类型可能有两种:

  • case 指定为明确的类型,如 float32 ,则 v 的类型就是 float32 ,等价于如下语句:
1
2
var v float32 = idx.(float32)
idx2 := int(v)
  • case 如果指定了多个类型,由于 v 无法同时是指定的这些类型,所以 v 只能为 interface{} 类型,所以以上语句变成了:
1
2
var v interface{} = idx
idx2 := int(v)

这里的直接强转,自然就通不过了,需要做一次 type assersion


参考资料

  1. Explain type assertions in go
  2. Type Assertion And Type Switch

茶歇驿站

一个可以让你停下来看一看,在茶歇之余给你帮助的小站,这里的内容主要是后端技术,个人管理,团队管理,以及其他个人杂想。