廢話
為什麼要給标題加上符号着重一下?難道程式設計語言中的思想不都是一樣的嗎?
的确,基本思想大同小異,各自實作的原理和設計哲學會有差異。
如果你之前沒有使用過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)
}