天天看点

Go语言中的<断言>

废话

为什么要给标题加上符号着重一下?难道编程语言中的思想不都是一样的吗?

的确,基本思想大同小异,各自实现的原理和设计哲学会有差异。

如果你之前没有使用过Go语言,那么你很自然的认为,Go语言中一定有

assert

之类的操作符或函数。答案是没有,这可能会让你很失望,你兴冲冲地看着某篇介绍Go语言的文章,当看到Go中的关键字如此之少的时候,你或许会赞叹:如此简洁!但当你正式编写代码却发现:这语言用着很不爽!没有

try catch

,没有泛型,没有断言,必须把起始括号放在语句末尾……

其实你陷入了一个误区,你以其他语言的视角来看待Go语言,自然得不到满意的结果。

问题

早晨我逛stackoverflow的时候看到了这个问题:

http://stackoverflow.com/questions/8103617/call-a-struct-and-its-method-by-name-in-go

我很自然的想用其他语言,如Python的字典,JS的对象字面量来对一个对象进行映射。

因此我这个学了几天Go的小财笔写了如下测试:

package main

import (
    "fmt"
    "reflect"
)

type MyStruct struct {
}

func (a *MyStruct) Action() {
    fmt.Println("Hello")
}

func main() {
    var M map[string]interface{}
    M = make(map[string]interface{}, 100)
    M["MyStruct"] = &MyStruct{}
    m := M["MyStruct"]
    fmt.Println(reflect.TypeOf(m)) // *main.MyStruct
    m.Action()
}           

结果是编译报错:

m.Action undefined (type interface {} is interface with no methods)

在线运行实例(一般人都认为没有golang.org这个网站,请自行解决访问方式)

https://play.golang.org/p/qnpHmY9S73

我很奇怪,

reflect.TypeOf

明明检测出类型是MyStruct,为什么没有方法呢?

我忽略了一个很明显的问题,同时也没有去看官方文档。

https://golang.org/ref/spec#Type_assertions

Stackoverflow上的朋友给了我完美的答案:http://stackoverflow.com/questions/36439733/how-to-referencing-an-object-with-map/36439925#36439925

我声明map的时候,声明的值是interface{}类型,因此,interface类型并没有方法。

这是官方文档上的例子:

var x interface{} = 7  // x has dynamic type int and value 7
i := x.(int)           // i has type int and value 7           

而解决的方法很简单,用Go语言提供的类型检测方法,同时也可作为断言的解决方案:

package main

import (
    "fmt"
    "reflect"
)

type MyStruct struct {
}

func (a *MyStruct) Action() {
    fmt.Println("Hello")
}

func main() {
    var M map[string]interface{}
    M = make(map[string]interface{}, 100)
    M["MyStruct"] = &MyStruct{}
    om := M["MyStruct"]            // the type of variable m is interface
    m := om.(*MyStruct)            // so asserts that value restored in m
    fmt.Println(reflect.TypeOf(m)) // *main.MyStruct
    m.Action()
}           

https://play.golang.org/p/mjxkAeRuDV

m := om.(*MyStruct)

  • 如果om是MyStruct类型,则将转换后的类型赋值给m。
  • 如果MyStruct被定义为一个接口,只要实现了这个接口的对象,此操作都会成功。

不过需要注意的是,如果false了,也就是不匹配,就会产生panic错误。

还有一种不产生错误的方法。

m, ok := om.(*MyStruct)           

使用两个变量接受返回值,如果ok为false,则m为零值,但是不会报错。

因此你可以这样来进行错误处理:

if !ok {
    fmt.Println("Type not matched!")
    os.Exit(1)
}           

继续阅读