介面定義與實作#

Go 的介面採隱式實作(鴨子型別,Duck Typing):

// 定義介面
type Writer interface {
    Write(data []byte) (int, error)
}

type Reader interface {
    Read(buf []byte) (int, error)
}

// 介面組合
type ReadWriter interface {
    Reader
    Writer
}

// 隱式實作
type FileBuffer struct {
    data []byte
}

func (f *FileBuffer) Write(data []byte) (int, error) {
    f.data = append(f.data, data...)
    return len(data), nil
}

func (f *FileBuffer) Read(buf []byte) (int, error) {
    n := copy(buf, f.data)
    return n, nil
}

// FileBuffer 自動實作了 Writer、Reader、ReadWriter

Go 的介面實作不需要 implements 關鍵字。只要型別實作了介面定義的所有方法,就自動滿足該介面。

空介面#

interface{} 是空介面,可以儲存任何型別的值:

var any interface{}

any = 42
any = "hello"
any = []int{1, 2, 3}

// Go 1.18+ 可以使用 any 作為 interface{} 的別名
var value any = "test"

介面值的內部結構#

介面值由兩部分組成:

// 概念
type iface struct {
    tab  *itab          // 型別資訊
    data unsafe.Pointer // 實際值的指標
}
var w Writer
var fb *FileBuffer

// w 的 tab 和 data 都是 nil
fmt.Println(w == nil)  // true

w = fb  // fb 是 nil 指標

// w 的 tab 指向 *FileBuffer 型別資訊
// w 的 data 是 nil
fmt.Println(w == nil)  // false!

即使把 nil 指標賦值給介面,介面值也不等於 nil。這是非常常見的陷阱,要避免把 nil 指標傳給介面型別。

判斷介面是否真正為空
func IsNil(i interface{}) bool {
    if i == nil {
        return true
    }
    v := reflect.ValueOf(i)
    switch v.Kind() {
    case reflect.Ptr, reflect.Map, reflect.Slice,
         reflect.Chan, reflect.Func, reflect.Interface:
        return v.IsNil()
    }
    return false
}