Golang中interface內(nèi)部構(gòu)造與面試真題分析

原創(chuàng)聲明
作者:劉丹冰Aceld, 微信公眾號(hào)同名

(1) interface的賦值問題

以下代碼能編譯過去嗎忍啤?為什么铸董?

package main

import (
    "fmt"
)

type People interface {
    Speak(string) string
}

type Stduent struct{}

func (stu *Stduent) Speak(think string) (talk string) {
    if think == "love" {
        talk = "You are a good boy"
    } else {
        talk = "hi"
    }
    return
}

func main() {
    var peo People = Stduent{}
    think := "love"
    fmt.Println(peo.Speak(think))
}

繼承與多態(tài)的特點(diǎn)

在golang中對(duì)多態(tài)的特點(diǎn)體現(xiàn)從語法上并不是很明顯吓著。

我們知道發(fā)生多態(tài)的幾個(gè)要素:

1习贫、有interface接口乳愉,并且有接口定義的方法兄淫。

2屯远、有子類去重寫interface的接口。

3捕虽、有父類指針指向子類的具體對(duì)象

那么慨丐,滿足上述3個(gè)條件,就可以產(chǎn)生多態(tài)效果泄私,就是房揭,父類指針可以調(diào)用子類的具體方法。

所以上述代碼報(bào)錯(cuò)的地方在var peo People = Stduent{}這條語句晌端, Student{}已經(jīng)重寫了父類People{}中的Speak(string) string方法捅暴,那么只需要用父類指針指向子類對(duì)象即可。

所以應(yīng)該改成var peo People = &Student{} 即可編譯通過咧纠。(People為interface類型蓬痒,就是指針類型)

(2) interface的內(nèi)部構(gòu)造(非空接口iface情況)

以下代碼打印出來什么內(nèi)容,說出為什么漆羔。

package main

import (
    "fmt"
)

type People interface {
    Show()
}

type Student struct{}

func (stu *Student) Show() {

}

func live() People {
    var stu *Student
    return stu
}

func main() {
    if live() == nil {
        fmt.Println("AAAAAAA")
    } else {
        fmt.Println("BBBBBBB")
    }
}

結(jié)果

BBBBBBB

分析:

我們需要了解interface的內(nèi)部結(jié)構(gòu)梧奢,才能理解這個(gè)題目的含義。

interface在使用的過程中钧椰,共有兩種表現(xiàn)形式

一種為空接口(empty interface)粹断,定義如下:

var MyInterface interface{}

另一種為非空接口(non-empty interface), 定義如下:

type MyInterface interface {
        function()
}

這兩種interface類型分別用兩種struct表示,空接口為eface, 非空接口為iface.
!](https://upload-images.jianshu.io/upload_images/11093205-390416d69864055e.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


空接口eface

空接口eface結(jié)構(gòu)嫡霞,由兩個(gè)屬性構(gòu)成瓶埋,一個(gè)是類型信息_type,一個(gè)是數(shù)據(jù)信息诊沪。其數(shù)據(jù)結(jié)構(gòu)聲明如下:

type eface struct {      //空接口
    _type *_type         //類型信息
    data  unsafe.Pointer //指向數(shù)據(jù)的指針(go語言中特殊的指針類型unsafe.Pointer類似于c語言中的void*)
}

_type屬性:是GO語言中所有類型的公共描述养筒,Go語言幾乎所有的數(shù)據(jù)結(jié)構(gòu)都可以抽象成 _type,是所有類型的公共描述端姚,type負(fù)責(zé)決定data應(yīng)該如何解釋和操作晕粪,type的結(jié)構(gòu)代碼如下:

type _type struct {
    size       uintptr  //類型大小
    ptrdata    uintptr  //前綴持有所有指針的內(nèi)存大小
    hash       uint32   //數(shù)據(jù)hash值
    tflag      tflag
    align      uint8    //對(duì)齊
    fieldalign uint8    //嵌入結(jié)構(gòu)體時(shí)的對(duì)齊
    kind       uint8    //kind 有些枚舉值kind等于0是無效的
    alg        *typeAlg //函數(shù)指針數(shù)組,類型實(shí)現(xiàn)的所有方法
    gcdata    *byte
    str       nameOff
    ptrToThis typeOff
}

data屬性: 表示指向具體的實(shí)例數(shù)據(jù)的指針渐裸,他是一個(gè)unsafe.Pointer類型巫湘,相當(dāng)于一個(gè)C的萬能指針void*


非空接口iface

iface 表示 non-empty interface 的數(shù)據(jù)結(jié)構(gòu)昏鹃,非空接口初始化的過程就是初始化一個(gè)iface類型的結(jié)構(gòu)尚氛,其中data的作用同eface的相同,這里不再多加描述洞渤。

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

iface結(jié)構(gòu)中最重要的是itab結(jié)構(gòu)(結(jié)構(gòu)如下)阅嘶,每一個(gè) itab 都占 32 字節(jié)的空間。itab可以理解為pair<interface type, concrete type> 。itab里面包含了interface的一些關(guān)鍵信息讯柔,比如method的具體實(shí)現(xiàn)抡蛙。

type itab struct {
  inter  *interfacetype   // 接口自身的元信息
  _type  *_type           // 具體類型的元信息
  link   *itab
  bad    int32
  hash   int32            // _type里也有一個(gè)同樣的hash,此處多放一個(gè)是為了方便運(yùn)行接口斷言
  fun    [1]uintptr       // 函數(shù)指針魂迄,指向具體類型所實(shí)現(xiàn)的方法
}

其中值得注意的字段粗截,個(gè)人理解如下:

  1. interface type包含了一些關(guān)于interface本身的信息,比如package path捣炬,包含的method慈格。這里的interfacetype是定義interface的一種抽象表示。
  2. type表示具體化的類型遥金,與eface的 type類型相同。
  3. hash字段其實(shí)是對(duì)_type.hash的拷貝蒜田,它會(huì)在interface的實(shí)例化時(shí)稿械,用于快速判斷目標(biāo)類型和接口中的類型是否一致。另冲粤,Go的interface的Duck-typing機(jī)制也是依賴這個(gè)字段來實(shí)現(xiàn)美莫。
  4. fun字段其實(shí)是一個(gè)動(dòng)態(tài)大小的數(shù)組,雖然聲明時(shí)是固定大小為1梯捕,但在使用時(shí)會(huì)直接通過fun指針獲取其中的數(shù)據(jù)厢呵,并且不會(huì)檢查數(shù)組的邊界,所以該數(shù)組中保存的元素?cái)?shù)量是不確定的傀顾。

所以襟铭,People擁有一個(gè)Show方法的,屬于非空接口短曾,People的內(nèi)部定義應(yīng)該是一個(gè)iface結(jié)構(gòu)體

type People interface {
    Show()  
}  
func live() People {
    var stu *Student
    return stu      
}     

stu是一個(gè)指向nil的空指針寒砖,但是最后return stu 會(huì)觸發(fā)匿名變量 People = stu值拷貝動(dòng)作,所以最后live()放回給上層的是一個(gè)People insterface{}類型嫉拐,也就是一個(gè)iface struct{}類型哩都。 stu為nil,只是iface中的data 為nil而已婉徘。 但是iface struct{}本身并不為nil.

所以如下判斷的結(jié)果為BBBBBBB

func main() {   
    if live() == nil {  
        fmt.Println("AAAAAAA")      
    } else {
        fmt.Println("BBBBBBB")
    }
}

(3) interface內(nèi)部構(gòu)造(空接口eface情況)

下面代碼結(jié)果為什么漠嵌?

func Foo(x interface{}) {
    if x == nil {
        fmt.Println("empty interface")
        return
    }
    fmt.Println("non-empty interface")
}
func main() {
    var p *int = nil
    Foo(p)
}

結(jié)果

non-empty interface

分析

不難看出,Foo()的形參x interface{}是一個(gè)空接口類型eface struct{}盖呼。

在執(zhí)行Foo(p)的時(shí)候儒鹿,觸發(fā)x interface{} = p語句,所以此時(shí) x結(jié)構(gòu)如下塌计。

所以 x 結(jié)構(gòu)體本身不為nil挺身,而是data指針指向的p為nil。


(4) inteface{}與*interface{}

ABCD中哪一行存在錯(cuò)誤锌仅?

type S struct {
}

func f(x interface{}) {
}

func g(x *interface{}) {
}

func main() {
    s := S{}
    p := &s
    f(s) //A
    g(s) //B
    f(p) //C
    g(p) //D
}

結(jié)果

B章钾、D兩行錯(cuò)誤
B錯(cuò)誤為: cannot use s (type S) as type *interface {} in argument to g:
    *interface {} is pointer to interface, not interface
    
D錯(cuò)誤為:cannot use p (type *S) as type *interface {} in argument to g:
    *interface {} is pointer to interface, not interface

看到這道題需要第一時(shí)間想到的是Golang是強(qiáng)類型語言墙贱,interface是所有g(shù)olang類型的父類 函數(shù)中func f(x interface{})interface{}可以支持傳入golang的任何類型,包括指針贱傀,但是函數(shù)func g(x *interface{})只能接受*interface{}

如果掌握interface構(gòu)造惨撇,建議看下一篇文章
使用Golang的interface接口設(shè)計(jì)原則


關(guān)于作者:

劉丹冰Aceld (微信公眾號(hào)同名)
mail: danbing.at@gmail.com
github: https://github.com/aceld
原創(chuàng)書籍: https://www.kancloud.cn/@aceld


文章推薦

開源軟件作品

(原創(chuàng)開源)Zinx-基于Golang輕量級(jí)服務(wù)器并發(fā)框架-完整版(附教程視頻)

(原創(chuàng)開源)Lars-基于C++負(fù)載均衡遠(yuǎn)程調(diào)度系統(tǒng)-完整版

精選文章

典藏版-Golang調(diào)度器GMP原理與調(diào)度全分析

典藏版-Golang三色標(biāo)記、混合寫屏障GC模式圖文全分析

最常用的調(diào)試 golang 的 bug 以及性能問題的實(shí)踐方法府寒?

Golang中的Defer必掌握的7知識(shí)點(diǎn)

Golang中的局部變量“何時(shí)棧?何時(shí)堆?”

使用Golang的interface接口設(shè)計(jì)原則

流魁衙?I/O操作?阻塞株搔?epoll?

深入淺出Golang的協(xié)程池設(shè)計(jì)

Go語言構(gòu)建微服務(wù)一站式解決方案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剖淀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纤房,更是在濱河造成了極大的恐慌纵隔,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炮姨,死亡現(xiàn)場(chǎng)離奇詭異捌刮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)舒岸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門绅作,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蛾派,你說我怎么就攤上這事俄认。” “怎么了洪乍?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵梭依,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我典尾,道長(zhǎng)役拴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任钾埂,我火速辦了婚禮河闰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘褥紫。我一直安慰自己姜性,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布髓考。 她就那樣靜靜地躺著部念,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上儡炼,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天妓湘,我揣著相機(jī)與錄音,去河邊找鬼乌询。 笑死榜贴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妹田。 我是一名探鬼主播唬党,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鬼佣!你這毒婦竟也來了驶拱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤晶衷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后房铭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡温眉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年缸匪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片类溢。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凌蔬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闯冷,到底是詐尸還是另有隱情砂心,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布蛇耀,位于F島的核電站辩诞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纺涤。R本人自食惡果不足惜译暂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望撩炊。 院中可真熱鬧外永,春花似錦、人聲如沸拧咳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祭衩,卻和暖如春灶体,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汪厨。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國打工赃春, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劫乱。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓织中,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親衷戈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狭吼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容