原創聲明
做者: 劉丹冰Aceld, 微信公衆號同名
如下代碼能編譯過去嗎?爲何?
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)) }
繼承與多態的特色git
在golang中對多態的特色體現從語法上並非很明顯。github
咱們知道發生多態的幾個要素:golang
一、有interface接口,而且有接口定義的方法。數組
二、有子類去重寫interface的接口。bash
三、有父類指針指向子類的具體對象服務器
那麼,知足上述3個條件,就能夠產生多態效果,就是,父類指針能夠調用子類的具體方法。微信
因此上述代碼報錯的地方在var peo People = Stduent{}
這條語句, Student{}
已經重寫了父類People{}
中的Speak(string) string
方法,那麼只須要用父類指針指向子類對象便可。數據結構
因此應該改爲var peo People = &Student{}
便可編譯經過。(People爲interface類型,就是指針類型)併發
如下代碼打印出來什麼內容,說出爲何。
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") } }
結果負載均衡
BBBBBBB
分析:
咱們須要瞭解interface
的內部結構,才能理解這個題目的含義。
interface在使用的過程當中,共有兩種表現形式
一種爲空接口(empty interface),定義以下:
var MyInterface interface{}
另外一種爲非空接口(non-empty interface), 定義以下:
type MyInterface interface { function() }
這兩種interface類型分別用兩種struct
表示,空接口爲eface
, 非空接口爲iface
.
空接口eface結構,由兩個屬性構成,一個是類型信息_type,一個是數據信息。其數據結構聲明以下:
type eface struct { //空接口 _type *_type //類型信息 data unsafe.Pointer //指向數據的指針(go語言中特殊的指針類型unsafe.Pointer相似於c語言中的void*) }
_type屬性:是GO語言中全部類型的公共描述,Go語言幾乎全部的數據結構均可以抽象成 _type,是全部類型的公共描述,type負責決定data應該如何解釋和操做,type的結構代碼以下:
type _type struct { size uintptr //類型大小 ptrdata uintptr //前綴持有全部指針的內存大小 hash uint32 //數據hash值 tflag tflag align uint8 //對齊 fieldalign uint8 //嵌入結構體時的對齊 kind uint8 //kind 有些枚舉值kind等於0是無效的 alg *typeAlg //函數指針數組,類型實現的全部方法 gcdata *byte str nameOff ptrToThis typeOff }
data屬性: 表示指向具體的實例數據的指針,他是一個unsafe.Pointer
類型,至關於一個C的萬能指針void*
。
iface 表示 non-empty interface 的數據結構,非空接口初始化的過程就是初始化一個iface類型的結構,其中data
的做用同eface
的相同,這裏再也不多加描述。
type iface struct { tab *itab data unsafe.Pointer }
iface結構中最重要的是itab結構(結構以下),每個 itab
都佔 32 字節的空間。itab能夠理解爲pair<interface type, concrete type>
。itab裏面包含了interface的一些關鍵信息,好比method的具體實現。
type itab struct { inter *interfacetype // 接口自身的元信息 _type *_type // 具體類型的元信息 link *itab bad int32 hash int32 // _type裏也有一個一樣的hash,此處多放一個是爲了方便運行接口斷言 fun [1]uintptr // 函數指針,指向具體類型所實現的方法 }
其中值得注意的字段,我的理解以下:
interface type
包含了一些關於interface自己的信息,好比package path
,包含的method
。這裏的interfacetype是定義interface的一種抽象表示。type
表示具體化的類型,與eface的 type類型相同。 hash
字段實際上是對_type.hash
的拷貝,它會在interface的實例化時,用於快速判斷目標類型和接口中的類型是否一致。另,Go的interface的Duck-typing機制也是依賴這個字段來實現。fun
字段實際上是一個動態大小的數組,雖然聲明時是固定大小爲1,但在使用時會直接經過fun指針獲取其中的數據,而且不會檢查數組的邊界,因此該數組中保存的元素數量是不肯定的。因此,People擁有一個Show方法的,屬於非空接口,People的內部定義應該是一個iface
結構體
type People interface { Show() }
func live() People { var stu *Student return stu }
stu是一個指向nil的空指針,可是最後return stu
會觸發匿名變量 People = stu
值拷貝動做,因此最後live()
放回給上層的是一個People insterface{}
類型,也就是一個iface struct{}
類型。 stu爲nil,只是iface
中的data 爲nil而已。 可是iface struct{}
自己並不爲nil.
因此以下判斷的結果爲BBBBBBB
:
func main() { if live() == nil { fmt.Println("AAAAAAA") } else { fmt.Println("BBBBBBB") } }
下面代碼結果爲何?
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) }
結果
non-empty interface
分析
不難看出,Foo()
的形參x interface{}
是一個空接口類型eface struct{}
。
在執行Foo(p)
的時候,觸發x interface{} = p
語句,因此此時 x結構以下。
因此 x 結構體自己不爲nil,而是data指針指向的p爲nil。
ABCD中哪一行存在錯誤?
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 }
結果
B、D兩行錯誤 B錯誤爲: cannot use s (type S) as type *interface {} in argument to g: *interface {} is pointer to interface, not interface D錯誤爲:cannot use p (type *S) as type *interface {} in argument to g: *interface {} is pointer to interface, not interface
看到這道題須要第一時間想到的是Golang是強類型語言,interface是全部golang類型的父類 函數中func f(x interface{})
的interface{}
能夠支持傳入golang的任何類型,包括指針,可是函數func g(x *interface{})
只能接受*interface{}
若是掌握interface
構造,建議看下一篇文章
使用Golang的interface接口設計原則
mail: danbing.at@gmail.com
github: https://github.com/aceld
原創書籍gitbook: http://legacy.gitbook.com/@aceld
(原創開源)Zinx-基於Golang輕量級服務器併發框架-完整版(附教程視頻)
(原創開源)Lars-基於C++負載均衡遠程調度系統-完整版