原創(chuàng)聲明
作者:劉丹冰Aceld, 微信公眾號同名
(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)的特點
在golang中對多態(tài)的特點體現(xiàn)從語法上并不是很明顯。
我們知道發(fā)生多態(tài)的幾個要素:
1、有interface接口,并且有接口定義的方法。
2、有子類去重寫interface的接口。
3、有父類指針指向子類的具體對象
那么,滿足上述3個條件,就可以產(chǎn)生多態(tài)效果,就是,父類指針可以調(diào)用子類的具體方法。
所以上述代碼報錯的地方在var peo People = Stduent{}這條語句, Student{}已經(jīng)重寫了父類People{}中的Speak(string) string方法,那么只需要用父類指針指向子類對象即可。
所以應該改成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),才能理解這個題目的含義。
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òu)成,一個是類型信息_type,一個是數(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負責決定data應該如何解釋和操作,type的結(jié)構(gòu)代碼如下:
type _type struct {
size uintptr //類型大小
ptrdata uintptr //前綴持有所有指針的內(nèi)存大小
hash uint32 //數(shù)據(jù)hash值
tflag tflag
align uint8 //對齊
fieldalign uint8 //嵌入結(jié)構(gòu)體時的對齊
kind uint8 //kind 有些枚舉值kind等于0是無效的
alg *typeAlg //函數(shù)指針數(shù)組,類型實現(xiàn)的所有方法
gcdata *byte
str nameOff
ptrToThis typeOff
}
data屬性: 表示指向具體的實例數(shù)據(jù)的指針,他是一個unsafe.Pointer類型,相當于一個C的萬能指針void*。

非空接口iface
iface 表示 non-empty interface 的數(shù)據(jù)結(jié)構(gòu),非空接口初始化的過程就是初始化一個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)如下),每一個 itab 都占 32 字節(jié)的空間。itab可以理解為pair<interface type, concrete type> 。itab里面包含了interface的一些關(guān)鍵信息,比如method的具體實現(xiàn)。
type itab struct {
inter *interfacetype // 接口自身的元信息
_type *_type // 具體類型的元信息
link *itab
bad int32
hash int32 // _type里也有一個同樣的hash,此處多放一個是為了方便運行接口斷言
fun [1]uintptr // 函數(shù)指針,指向具體類型所實現(xiàn)的方法
}
其中值得注意的字段,個人理解如下:
-
interface type包含了一些關(guān)于interface本身的信息,比如package path,包含的method。這里的interfacetype是定義interface的一種抽象表示。 -
type表示具體化的類型,與eface的 type類型相同。 -
hash字段其實是對_type.hash的拷貝,它會在interface的實例化時,用于快速判斷目標類型和接口中的類型是否一致。另,Go的interface的Duck-typing機制也是依賴這個字段來實現(xiàn)。 -
fun字段其實是一個動態(tài)大小的數(shù)組,雖然聲明時是固定大小為1,但在使用時會直接通過fun指針獲取其中的數(shù)據(jù),并且不會檢查數(shù)組的邊界,所以該數(shù)組中保存的元素數(shù)量是不確定的。

所以,People擁有一個Show方法的,屬于非空接口,People的內(nèi)部定義應該是一個iface結(jié)構(gòu)體
type People interface {
Show()
}

func live() People {
var stu *Student
return stu
}
stu是一個指向nil的空指針,但是最后return stu 會觸發(fā)匿名變量 People = stu值拷貝動作,所以最后live()放回給上層的是一個People insterface{}類型,也就是一個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{}是一個空接口類型eface struct{}。

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

所以 x 結(jié)構(gòu)體本身不為nil,而是data指針指向的p為nil。
(4) inteface{}與*interface{}
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
}
結(jié)果
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是所有g(shù)olang類型的父類 函數(shù)中func f(x interface{})的interface{}可以支持傳入golang的任何類型,包括指針,但是函數(shù)func g(x *interface{})只能接受*interface{}
如果掌握interface構(gòu)造,建議看下一篇文章
使用Golang的interface接口設(shè)計原則
關(guān)于作者:
劉丹冰Aceld (微信公眾號同名)
mail: danbing.at@gmail.com
github: https://github.com/aceld
原創(chuàng)書籍: https://www.kancloud.cn/@aceld
文章推薦
開源軟件作品
(原創(chuàng)開源)Zinx-基于Golang輕量級服務器并發(fā)框架-完整版(附教程視頻)
(原創(chuàng)開源)Lars-基于C++負載均衡遠程調(diào)度系統(tǒng)-完整版
精選文章
典藏版-Golang調(diào)度器GMP原理與調(diào)度全分析