7.3. 类型断言总结

7.1节介绍了如何从类型为 interface{} 的接口值中通过类型断言获取它所包含的具体值,该用法对于非空接口值依然成立,例如:

// 版权 @2023 凹语言 作者。保留所有权利。

type Printer interface {
    Print()
}

type T1 struct {
    i: i32
}

func T1.Print { println("This is T1, this.i:", this.i) }

type T2 struct {
    s: string
}

func T2.Print { println("This is T2, this.s:", this.s) }

func doConcrete(p: Printer) {
    switch v := p.(type) {
    case *T1:
        v.Print() // 方法直接调用,而非接口调用

    case *T2:
        v.Print()
    }
}

func main {
    v1 := T1{i: 42}
    doConcrete(&v1) // This is T1, this.i: 42

    v2 := T2{s: "hello"}
    doConcrete(&v2) // This is T2, this.s: hello
}

注意函数 doConcretev.Print() 是直接调用,而非接口调用,因为在 case *T1 分支中,v 的类型是 *T1。另外,非空接口值也可以通过 v, ok = iface.(Type) 形式进行具体类型断言,这与7.1节中空接口值的对应用法一致。

实际上类型断言的用法还不仅于此,在某些情况下,一个具体类型 *T 可能同时满足多个接口 I1I2,那么当一个 I1 的接口值中包含的具体值类型为 *T 时,可以在该接口值上通过类型断言,直接获取一个类型为 I2 的接口值,例如:

// 版权 @2023 凹语言 作者。保留所有权利。

type I1 interface {
    f1()
}

type I2 interface {
    f2()
}

type T struct {
    i: i32
}

func T.f1 { println("T.f1(), T.i:", this.i) }

func T.f2 { println("T.f2(), T.i:", this.i) }

func main {
    v1 := T{i: 42}

    i1: I1 = &v1
    i1.f1() // T.f1(), T.i: 42

    i2, ok := i1.(I2) // 断言为另一个接口
    if ok {
        i2.f2() // T.f2(), T.i: 42
    }
}

这种用法一般常见于从 interface{} 接口值中获取非空接口。

除了形如 v, ok = iface.(Type) 的类型断言外,还有另一种模式的类型断言:

    v = iface.(Type)

该模式取消了操作成功标志的返回值 ok,只返回被断言类型的值。如果类型断言失败,则会触发运行时异常,建议仅在完全确认断言不会失败的情况下才使用该模式。