golang interface判斷爲空nil

要判斷interface 空的問題,首先看下其底層實現。golang

interface 底層結構

根據 interface 是否包含有 method,底層實現上用兩種 struct 來表示:iface 和 eface。eface表示不含 method 的 interface 結構,或者叫 empty interface。對於 Golang 中的大部分數據類型均可以抽象出來 _type 結構,同時針對不一樣的類型還會有一些其餘信息。ide

1.eface

type eface struct {
    _type *_type
    data  unsafe.Pointer
}

type _type struct {
    size       uintptr // type size
    ptrdata    uintptr // size of memory prefix holding all pointers
    hash       uint32  // hash of type; avoids computation in hash tables
    tflag      tflag   // extra type information flags
    align      uint8   // alignment of variable with this type
    fieldalign uint8   // alignment of struct field with this type
    kind       uint8   // enumeration for C
    alg        *typeAlg  // algorithm table
    gcdata    *byte    // garbage collection data
    str       nameOff  // string form
    ptrToThis typeOff  // type for pointer to this type, may be zero
}

2.iface

iface 表示 non-empty interface 的底層實現。相比於 empty interface,non-empty 要包含一些 method。method 的具體實現存放在 itab.fun 變量裏。若是 interface 包含多個 method,這裏只有一個 fun 變量怎麼存呢?這個下面再細說。學習

type iface struct {
    tab  *itab
    data unsafe.Pointer
}

// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.
type itab struct {
    inter  *interfacetype
    _type  *_type
    link   *itab
    bad    int32
    inhash int32      // has this itab been added to hash?
    fun    [1]uintptr // variable sized
}

歸納起來,接口對象由接口表 (interface table) 指針和數據指針組成,或者說由動態類型和動態值組成。ui

struct Iface
{
    Itab* tab;
    void* data;
};

struct Itab
{
    InterfaceType* inter;
    Type* type;
    void (*fun[])(void);
};

接口表存儲元數據信息,包括接口類型、動態類型,以及實現接口的方法指針。不管是反射仍是經過接口調用方法,都會用到這些信息。this

再來看下nil的定義。指針

nil的定義

// nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type.

var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

也就是說,只有pointer, channel, func, interface, map, or slice 這些類型的值才能夠是nil.code

如何斷定interface裏面的動態值是否空

對於一個接口的零值就是它的類型和值的部分都是nil。orm

一個接口值基於它的動態類型被描述爲空或非空。對象

例如,接口

var w io.Writer

通常狀況下,經過使用w==nil或者w!=nil來判讀接口值是否爲空,只是判斷了動態類型,而沒有判斷動態值。

例如,下面的例子。

package main

import ("fmt")


func main(){

       var a interface{} = nil // tab = nil, data = nil
       var b interface{} = (*int)(nil) // tab 包含 *int 類型信息, data = nil

       fmt.Println(a==nil)
       fmt.Println(b==nil)
}

output:

true
false

上面代碼中,接口b的動態類型爲*int, 而動態值爲nil,直接使用等於號沒法判斷。

因此不能直接經過與nil比較的方式判斷動態值是否爲空。

那如何判斷動態值是否爲空?

能夠藉助反射來判斷。

func IsNil(i interface{}) bool {
    defer func() {
        recover()
    }()
    vi := reflect.ValueOf(i)
    return vi.IsNil()
}

其中,IsNil定義以下:

func (v Value) IsNil() bool

參數v必須是chan, func, interface, map, pointer, or slice,不然會panic。

若是調用IsNil的不是一個指針,會出現異常,須要捕獲異常。
或者修改爲這樣:

func IsNil(i interface{}) bool {
    vi := reflect.ValueOf(i)
    if vi.Kind() == reflect.Ptr {
        return vi.IsNil()
    }
    return false
}

總結

一個接口包括動態類型和動態值。
若是一個接口的動態類型和動態值都爲空,則這個接口爲空的。

參考

https://golang.org/src/builtin/builtin.go?h=var+nil+Type#L101

《Go學習筆記--雨痕》

http://legendtkl.com/2017/07/01/golang-interface-implement/

https://www.jianshu.com/p/97bfe8104e03

相關文章
相關標籤/搜索