天天看點

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)
}           

繼續閱讀