废话
为什么要给标题加上符号着重一下?难道编程语言中的思想不都是一样的吗?
的确,基本思想大同小异,各自实现的原理和设计哲学会有差异。
如果你之前没有使用过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)
}
这其实就是Go中简洁但被诟病的value, err := F()
设计哲学。有人调侃:当用其他语言在编写功能的时候,用Go已经把功能写好了,但当用其他语言写的产品已经在测试了,用Go语言的人还在写if err { ... }
结论
英文不提高,不首先去看官网文档,你都是个屁。
有疑问加站长微信联系(非本文作者)