Go 类型断言

断言语法、comma-ok、type switch、以及接口的运行时类型。

Intermediate
Key Points`x.(T)` 断言接口值的具体类型;不带 `ok` 时失败会 panic。

两种形式

Go
var x any = "hello"

// 形式一:失败 panic
s := x.(string)
fmt.Println(s)

// 形式二:comma-ok,失败不 panic
n, ok := x.(int)
fmt.Println(n, ok)

// fmt.Println(x.(int))     // panic: interface conversion
Output
hello
0 false

什么时候需要断言

Go
// 从 map[string]any 里取值(JSON 解析常见)
var data any = map[string]any{
	"name": "tom",
	"age":  30,
}

m := data.(map[string]any)
if name, ok := m["name"].(string); ok {
	fmt.Println("名字", name)
}

// 数字在 JSON 里默认是 float64
if age, ok := m["age"].(float64); ok {
	fmt.Println("年龄", int(age))
}
Output
名字 tom
年龄 30

encoding/json decodes numbers as float64, never int, so .(int) always fails — the classic bug with dynamic JSON. Decode into a struct, or enable json.Decoder.UseNumber().

type switch

Go
func kind(v any) string {
	switch x := v.(type) {
	case nil:
		return "nil"
	case int, int64:
		return "整数"
	case float64:
		return fmt.Sprintf("浮点 %.1f", x)
	case string:
		return "字符串长度 " + strconv.Itoa(len(x))
	case []any:
		return fmt.Sprintf("数组 %d 个元素", len(x))
	default:
		return fmt.Sprintf("未知 %T", x)
	}
}

fmt.Println(kind(1))
fmt.Println(kind(3.14))
fmt.Println(kind("go"))
fmt.Println(kind([]any{1, 2}))
Output
整数
浮点 3.1
字符串长度 2
数组 2 个元素

Inside switch x := v.(type), x has a different type per case: in a multi-type case (int, int64) it stays any; in a single-type case it is that concrete type.

断言接口

Go
type Stringer interface{ String() string }

func toString(v any) string {
	// 断言的是「它是否实现了某接口」,不是具体类型
	if s, ok := v.(Stringer); ok {
		return s.String()
	}
	return fmt.Sprint(v)
}

type Temp float64

func (t Temp) String() string { return fmt.Sprintf("%.1f°C", float64(t)) }

fmt.Println(toString(Temp(36.6)))
fmt.Println(toString(42))
Output
36.6°C
42

This is the most valuable use: runtime capability probing. io.Copy asserts WriterTo/ReaderFrom and takes the fast path when available, else the generic buffered copy.

断言 vs 反射

AspectType assertionreflect
适用已知几个候选类型完全未知的任意类型
性能快(一次比较)慢(10~100 倍)
可读性好差
典型场景JSON 解析、插件钩子ORM、序列化框架
Prefer assertions over reflection. Reflective code escapes compile-time checks, panics far from the cause, and is an order of magnitude slower. Reserve it for frameworks — ORMs, serialisers, validators.

断言失败的排查

Go
// 别写这种,出错时完全不知道是什么类型
v := data.(map[string]any)

// 写成这样,报错信息带上实际类型
m, ok := data.(map[string]any)
if !ok {
	return fmt.Errorf("期望 map[string]any,实际 %T", data)
}

Friends & Partners