Golang Interface 詳解(下)

iface 和 eface 的區別

  在 Go 語言中,iface 和 eface 是表示接口類型和空接口類型的內部數據結構。

  iface 表示一個具體的接口類型,包含了指向接口表的指針和指向數據的指針。接口表中存儲了該接口類型的方法集信息,數據指針則指向實現了該接口的具體類型的值。

  eface 則是一個空接口類型,包含了指向類型信息的指針和指向數據的指針。它可以表示任何類型的值。

  最大的區別在於 iface 描述的接口包含方法,而 eface 則是不包含任何方法的空接口:interface{}。
  從源碼層面看一下:

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

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

  iface 內部維護兩個指針,tab 指向一個 itab 實體, 它表示接口的類型以及賦給這個接口的實體類型。data 則指向接口具體的值,一般而言是一個指向堆內存的指針。

  相比 iface,eface 就比較簡單了。只維護了一個 _type 字段,表示空接口所承載的具體的實體類型。data 描述了具體的值。

接口的動態類型和動態值是什麼

  從前面 iface 源碼裏可以看到:iface 包含兩個字段:tab 是接口表指針,指向類型信息;data 是數據指針,則指向具體的數據。它們分別被稱爲動態類型和動態值。

  首先明確一下接口值包括動態類型和動態值。接口值的零值是指動態類型和動態值都爲 nil。當僅且當這兩部分的值都爲 nil 的情況下,這個接口值就纔會被認爲 接口值 == nil。

  讓我們看一個例子:

package main

import "fmt"

type Coder interface {
 code()
}

type Gopher struct {
 name string
}

func (g Gopher) code() {
 fmt.Printf("%s is coding\n", g.name)
}

func main() {
 var c Coder
 fmt.Println(c == nil)  //true
 fmt.Printf("c: %T, %v\n", c, c)

 var g *Gopher
 fmt.Println(g == nil) //true

 c = g
 fmt.Println(c == nil) //false
 fmt.Printf("c: %T, %v\n", c, c)
}

  結果如下:  一開始,c 的 動態類型和動態值都爲 nil,g 也爲 nil,當把 g 賦值給 c 後,c 的動態類型變成了 *main.Gopher,僅管 c 的動態值仍爲 nil,但是當 c 和 nil 作比較的時候,結果就是 false 了。

  我們知道,Go 語言中不允許隱式類型轉換,也就是說 = 兩邊,不允許出現類型不相同的變量。

  類型轉換、類型斷言本質都是把一個類型轉換成另外一個類型。不同之處在於,類型斷言是對接口變量進行的操作。

  類型轉換是將一個變量的類型轉換爲另一個類型。在 Go 中,類型轉換使用如下語法:

//類型轉換語法
destinationType(variable)

// 例如:將float64類型的變量轉換爲int類型
var myFloat float64 = 3.14
var myInt int
myInt = int(myFloat)

注意,當進行類型轉換時,可能會丟失一些信息(例如,將浮點數轉換爲整數時,小數部分將被捨去)。

  類型斷言用於在接口類型和具體類型之間進行轉換。類型斷言的語法如下:

value, ok := interfaceVariable.(ConcreteType)

//如果斷言成功,ok 的值將爲 true,並且 value 將包含接口變量的具體類型值。如果斷言失敗,ok 的值將爲 false,並且 value 將爲具體類型的零值。

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

//你可以使用類型斷言來檢查一個 Shape 類型的變量是否包含一個 Circle 類型的值:
func main() {
    var myShape Shape
    myShape = Circle{Radius: 5}

    if circle, ok := myShape.(Circle); ok {
        fmt.Println("The shape is a circle with radius", circle.Radius)
    } else {
        fmt.Println("The shape is not a circle")
    }
}

參考資料

【有彙編分析,不錯】http://legendtkl.com/2017/07/01/golang-interface-implement/

【interface 源碼解讀 很不錯 包含反射】http://wudaijun.com/2018/01/go-interface-implement/

【類型轉換和斷言】https://www.cnblogs.com/zrtqsk/p/4157350.html

【斷言】https://studygolang.com/articles/11419

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/jC3eQYLFx_sn35zKmbI6ww