引入

接口值可以存放任意类型的值,但有时候需要知道它具体是什么类型,才能做进一步操作。类型断言就是用来从接口值中提取具体类型和具体值的。

正文

定义

定义

类型断言用于从接口值中提取出具体类型的值,或判断接口值是否实现了某个更具体的接口。语法是 x.(T)x 必须是接口类型。配合逗号 ok 模式可以安全判断,配合 type switch 可以一次处理多种类型。

语法

// 直接断言(失败会 panic)
v := x.(T)
 
// 逗号 ok 模式(安全判断)
v, ok := x.(T)
 
// type switch(多类型分支)
switch v := x.(type) {
case int:
    // v 是 int
case string:
    // v 是 string
}

例子

直接断言

var i any = "hello"
s := i.(string)
fmt.Println(s) // hello

断言成功直接拿到值,断言失败会 panic

逗号 ok 模式

var i any = "hello"
 
s, ok := i.(string)
fmt.Println(s, ok) // hello true
 
n, ok := i.(int)
fmt.Println(n, ok) // 0 false

v, ok := x.(T) 判断,okfalse 时不会 panic,v 是零值。

断言为接口

type Writer interface {
    Write()
}
 
type Speaker interface {
    Speak()
}
 
type Person struct{}
 
func (p Person) Speak() { fmt.Println("hi") }
 
func main() {
    var i any = Person{}
 
    s, ok := i.(Speaker)
    fmt.Println(ok) // true
    s.Speak()       // hi
 
    _, ok = i.(Writer)
    fmt.Println(ok) // false
}

类型断言不仅能断言为具体类型,还能断言为另一个接口,判断是否实现了更多方法。

type switch

func describe(i any) {
    switch v := i.(type) {
    case int:
        fmt.Println("整数:", v)
    case string:
        fmt.Println("字符串:", v)
    case bool:
        fmt.Println("布尔:", v)
    default:
        fmt.Printf("其他类型: %T\n", v)
    }
}
 
func main() {
    describe(42)      // 整数: 42
    describe("hello") // 字符串: hello
    describe(true)    // 布尔: true
    describe(3.14)    // 其他类型: float64
}

type switchi.(type) 做分支判断,每个 casev 的类型自动变成对应的具体类型

type switch 多类型匹配

func check(i any) {
    switch i.(type) {
    case int, float64:
        fmt.Println("数字类型")
    case string:
        fmt.Println("字符串类型")
    default:
        fmt.Println("其他")
    }
}
 
check(42)    // 数字类型
check(3.14)  // 数字类型
check("hi")  // 字符串类型

一个 case 可以写多个类型,但此时 v 的类型退化为 any

接口值为 nil 时断言

var i any // nil
 
_, ok := i.(string)
fmt.Println(ok) // false

接口值为 nil 时,逗号 ok 模式返回 false,不会 panic。直接断言会 panic。

常见场景:错误处理

type MyError struct {
    Code int
    Msg  string
}
 
func (e *MyError) Error() string {
    return fmt.Sprintf("error %d: %s", e.Code, e.Msg)
}
 
func doSomething() error {
    return &MyError{Code: 404, Msg: "not found"}
}
 
func main() {
    err := doSomething()
    if me, ok := err.(*MyError); ok {
        fmt.Println("错误码:", me.Code) // 404
        fmt.Println("信息:", me.Msg)    // not found
    }
}

error 是接口,用类型断言可以判断错误的具体类型,做不同处理。

常见写法

写法说明
v := x.(T)直接断言,失败 panic
v, ok := x.(T)安全断言,失败不 panic
switch v := x.(type)type switch 多类型分支
case int, float64:多类型匹配
x.(Interface)断言为另一个接口

特点

  • 类型断言的 x 必须是接口类型,不能对具体类型用
  • 直接断言失败会 panic,生产代码建议用逗号 ok 模式
  • type switch 适合处理多种类型的场景,比 if-else 清晰
  • 可以断言为具体类型,也可以断言为更具体的接口
  • 接口值为 nil 时断言返回 false,不会 panic
  • error 接口的类型断言是 Go 错误处理的常见模式

理解

类型断言就是”打开接口值的盒子,看看里面到底是什么类型”。安全写法用逗号 ok 模式,不成功也不崩。多种类型用 type switch 一次搞定。类型断言让接口变得实用,拿到接口值后能还原成具体类型来操作。

引出

会断言类型了,那接口在实际项目中怎么设计才好?接下来看 Go 接口最佳实践,学习小接口、接口组合和标准库里的经典接口设计。