golang的空接口(interface {})類型判斷 2021-08-03

萬物皆可interface{}

在go里面轧简,任何數據類型的實例變量都可以認為實現了一個空接口,即interface{}這個類型可以“容納”任何其他的數據類型匾二,這給go的泛型提供了一種實現

注意哮独,interface{}是一個類型,而interface只是一個關鍵字
我們知道察藐,利用type可以重新給已有類型重命名皮璧,就像

type iface interface{}
var i iface

而不能夠

type iface interface

interface{}的“泛型”

例如

package main

import "fmt"

func main() {    
    var i []interface{}
    i = append(i, 1)
    i = append(i, "23")
    i = append(i, []int{4,5,6})
    fmt.Println(i)
}

輸出

[1 23 [4 5 6]]

這就實現了類似python中的list的功能,可以接納不同類型的元素分飞。但是悴务,需要注意的是,當這些 int string 和 slice 元素被append到i之后譬猫,它們也無一例外的被轉化為了interface{}類型讯檐。這是顯然的,作為強類型語言染服,[]interface{}類型的i自然只能接納interface{}類型的元素

那么别洪,這里就涉及到一個問題,就是不同類型的變量通過interface{}類型塞到一起柳刮,再取出來的時候怎么恢復原來的類型挖垛?

空接口(interface{})的類型判斷

有3種方式

  • type assert 類型斷言
    斷言就是對接口變量的類型進行檢查
    value, ok := element.(T)
    element是interface變量,T是要斷言的類型诚亚,ok是一個bool類型
    如果斷言成功晕换,ok即為true,說明element可以轉化為T類型站宗,轉化后的值賦值給value
package main

import "fmt"

func main() {
    container := []interface{}{}
    m1 := make(map[int]string)
    m2 := make(map[string]string)
    m1[1] = "1"
    m2["2"] = "2"
    container = append(container, m1)
    container = append(container, m2)
    fmt.Println(container)
    for _, m := range(container) {
        val, ok := m.(map[int]string)
        if ok {
            fmt.Println("map[int]string", val)
        }
        newval, ok := m.(map[string]string)
        if ok{
            fmt.Println("map[string]string", newval)
        }
    }
}

執(zhí)行結果為

[map[1:1] map[2:2]]
map[int]string map[1:1]
map[string]string map[2:2]
  • 使用反射機制
    【核心代碼】
retType := reflect.TypeOf(unknow)
val := reflect.ValueOf(unknow)

例子

package main

import "fmt"
import "reflect"

func main() {
    container := []interface{}{}
    m1 := make(map[int]string)
    m2 := make(map[string]string)
    m1[1] = "1"
    m2["2"] = "2"
    container = append(container, m1)
    container = append(container, m2)
    fmt.Println(container)
    for _, m := range(container) {
        retType := reflect.TypeOf(m)
        val := reflect.ValueOf(m)
        fmt.Println(retType, val)
    }
}

實際上闸准,一個結構體對象作為一個interface{}對象后,要通過反射獲取它原來的字段名梢灭、字段值和標簽夷家,還需要做一些工作蒸其,案例如下

package main

import "fmt"
import "reflect"

func main() {
    result := f1()
    retType := reflect.TypeOf(result)
    val := reflect.ValueOf(result)
    fmt.Printf("name:'%v' kind:'%v'\n", retType.Name(), retType.Kind()) //name:'' kind:'struct' 
    // 通過reflect.Type.FieldByName找到字段標簽
    if namefield, ok := retType.FieldByName("Name"); ok {
        fmt.Println(namefield.Tag) // json:"name"
    }
    
    
    fmt.Println(val, reflect.TypeOf(val).Kind())  // {alice 10} struct
    // 通過reflect.Value.FieldByName找到字段值
    v := val.FieldByName("Name").String()
    fmt.Println(v) // alice
    fmt.Println(reflect.TypeOf(v).Kind()) // string
}

func f1() interface{} {
    // 返回一個interface{}類型的匿名結構體實例
    return struct{
        Name string `json:"name"`
        Age int
    }{
        Name: "alice",
        Age: 10,
    }
}

關于反射參考https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-reflect/

  • type關鍵字判斷

type switch compares types instead of values. You can use this to discover the type of an interface value. In this example, the variable t will have the type corresponding to its clause.

注意,.(type)必須用于switch case中
switch unknow.(type){
case string:
//string類型todo
case int:
//int類型todo
}

package main

import "fmt"

func main() {
    container := []interface{}{}
    m1 := make(map[int]string)
    m2 := make(map[string]string)
    m1[1] = "1"
    m2["2"] = "2"
    container = append(container, m1)
    container = append(container, m2)
    fmt.Println(container)
    for _, m := range(container) {
        switch m.(type){
            case map[int]string:
                // 下面這行的寫法是錯誤的库快,因為m的type還是interface {}
                // fmt.Println("map[int]string", m[1])
                // m進行類型轉換
                v := m.(map[int]string)
                fmt.Println("map[int]string", v[1])
            case map[string]string:
                v := m.(map[string]string)
                fmt.Println("map[int]string", v["2"])
        }
        
    }
}

結果

[map[1:1] map[2:2]]
map[int]string 1
map[int]string 2

注意摸袁,type switch這種方法有一個比較隱蔽的坑:

我們知道,一個switch的case可以支持多個expression义屏,如:

switch time.Now().Weekday() {
    case time.Saturday, time.Sunday:    // 多個expression
        fmt.Println("It's the weekend")
    default:
        fmt.Println("It's a weekday")
    }

那么在type switch中靠汁,如果一個case包含了多個expression,那么實際上在這個case的clause里面得不到具體的類型闽铐,而仍然是一個interface{}蝶怔,如:

package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
        case int, int32, int64:
        fmt.Println(v)
        if v != 0{
            fmt.Println(v, "!=0")
        }
        //fmt.Printf("Twice %v is %v\n", v, v*2)  // 這行代碼會報錯,因為這個case判斷了3個類型兄墅,v仍然是空接口interface{}踢星,interface{}和2(int)不同類型不能相乘
        case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
        default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    var a int
    var b int32
    var c int64
    fmt.Println("test int")
    do(a)
    fmt.Println("test int32")
    do(b)
    fmt.Println("test int64")
    do(c)
    fmt.Println("test string")
    do("hello")
    fmt.Println("test bool")
    do(true)
}
輸出
test int
0
test int32
0
0 !=0
test int64
0
0 !=0
test string
"hello" is 5 bytes long
test bool
I don't know about type bool!

可以看到,在case int, int32, int64這個clause中隙咸,v為空接口interface{}類型沐悦,而數字0為int類型
在空接口參與的比較中,首先會比較值的類型五督,然后再比較值
所以當v這個空接口保存int32或者int64的時候藏否,就會出現所謂的0 !=0的情況

關于空接口的比較樣例如下:

package main

import "fmt"

func main() {
    var aa interface{}
    aa = int64(0)
    var b int
    fmt.Println(aa==b) // false
    fmt.Println(aa==0) // false
    fmt.Println(aa==int64(0)) // true
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市概荷,隨后出現的幾起案子秕岛,更是在濱河造成了極大的恐慌,老刑警劉巖误证,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件继薛,死亡現場離奇詭異,居然都是意外死亡愈捅,警方通過查閱死者的電腦和手機遏考,發(fā)現死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓝谨,“玉大人灌具,你說我怎么就攤上這事∑┪祝” “怎么了咖楣?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芦昔。 經常有香客問我诱贿,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任珠十,我火速辦了婚禮料扰,結果婚禮上,老公的妹妹穿的比我還像新娘焙蹭。我一直安慰自己晒杈,他們只是感情好,可當我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布孔厉。 她就那樣靜靜地躺著拯钻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撰豺。 梳的紋絲不亂的頭發(fā)上说庭,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天,我揣著相機與錄音郑趁,去河邊找鬼。 笑死姿搜,一個胖子當著我的面吹牛寡润,可吹牛的內容都是我干的弓千。 我是一名探鬼主播审胸,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼励七!你這毒婦竟也來了致份?” 一聲冷哼從身側響起变抽,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎氮块,沒想到半個月后绍载,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡滔蝉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年击儡,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝠引。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡阳谍,死狀恐怖,靈堂內的尸體忽然破棺而出螃概,到底是詐尸還是另有隱情矫夯,我是刑警寧澤,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布吊洼,位于F島的核電站训貌,受9級特大地震影響,放射性物質發(fā)生泄漏融蹂。R本人自食惡果不足惜旺订,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一弄企、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧区拳,春花似錦拘领、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至笆凌,卻和暖如春圣猎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乞而。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工送悔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爪模。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓欠啤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屋灌。 傳聞我的和親對象是個殘疾皇子洁段,可洞房花燭夜當晚...
    茶點故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內容