這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
什麼是介面
介面是golang最重要的特性之一,在Go中,介面interface其實和其他語言的介面意思沒什麼區別。interface理解其為一種類型的規範或者約定。一種類型是不是“實現”了一個介面呢?就看這種類型是不是實現了介面中定義的所有方法。接⼝是一個或多個方法簽名的集合,任何非介面類型只要擁有與之對應的全部方法實現 (包括相同的名稱、參數列表以及傳回值。),就表示它"實現" 了該介面,無需顯式在該類型上添加介面聲明。此種方式又被稱作Duck Type。
介面的執行個體化
介面是可被執行個體化的類型,而不僅僅是語言上的約束規範。當我們建立介面變數時,將會為其分配記憶體,並將賦值給它的對象拷貝儲存。將對象賦值給介面變數時,會發生值拷貝行為。沒錯,介面內部儲存的是指向這個複製品的指標。而且我們無法修改這個複製品的狀態,也無法擷取其指標。
package mainimport ("fmt")type Painter interface {Draw()}type Xiaowang struct {}func (Xiaowang) Draw() {fmt.Println("i am drawing a paper.") //畫畫}func main() {var xw Xiaowangvar painter Painter = xwpainter.Draw()var painter2 Painter = &xwpainter2.Draw()painter3 := Painter(xw)painter3.Draw()painter4 := Painter(&xw)painter4.Draw()}
此程式中,以上的執行個體化方法都正確,因為runtime會幫我們調整*和非*。
但如果將Draw前面的接收者改為*Xiaowang,即:
func (*Xiaowang) Draw() {fmt.Println("i am drawing a paper.") //畫畫}
那麼非指標的執行個體化就會出錯,提示如下:
.\main.go:25: cannot use xw (type Xiaowang) as type Painter in assignment:Xiaowang does not implement Painter (Draw method requires pointer receiver).\main.go:29: cannot convert xw (type Xiaowang) to type Painter:Xiaowang does not implement Painter (Draw method requires pointer receiver)
介面的面向類型
在golang中,type很好地實現了一對多,也就是利用這個type關鍵字可以定義出多種多樣的不同類型。而interface很好地支援了多對一,即任何類型都可以被interface類型接收。
Go語言裡可以使用type關鍵字來把一個類型來轉換成另外一個類型而保持資料的本質不變,例如:
type Age inttype Height inttype Grade int
type絕不只是對應於C/C++中的typedef,
它不是用於定義一系列的
別名。
它的作用是定義
一系列互不相干的行為特徵:通過這些互不相干的行為特徵, 本質上同一的事物表現出不同事物的特徵。以上面的代碼為例:int還是那個int,Age、Height、Grade都是int,但通過type以int為基礎類型定義出的不同類型,就完全不相干了;Age、Height、Grade是完全不同的類型(也就是面向不同的對象)。我們可以分別為Age、Height、Grade定義出下列不同的行為(表示為方法或者函數):
func (a Age) Old() bool { return a > 50}func (l Length) NeedTicket() bool { return l > 120}func (g Grade) Pass() bool { return g >= 60}
這樣就區分開了以int為基礎類型的各種衍生類型。再看一個複雜一些的例子:
type Painter interface { Draw()}type Cowboy interface { Draw()}type Xiaoming struct {}type XiaomingAsPainter Xiaomingfunc (p *XiaomingAsPainter) Draw() { fmt.Println("I'm painting.")}func main() { var xm Xiaoming var painter Painter = (*XiaomingAsPainter)(&xm) painter.Draw()}
上面的兩個介面都包括了Draw()方法,如果類型小明只是一個畫家,那麼怎麼實現畫家介面呢?這裡就需要理解一個概念:
interface並不是孤立存在的,它是type的抽象。而同一個事物/資料可以有多個type。小明是個人(這裡實現為一個struct),畫家只是他的一種身份,因此把畫家的行為特性獨立出來作為一個type,同時也是Painter介面的實現。如果想讓他以畫家的身份示人,只要把小明轉換到畫家的身份就可以(通過語句"var painter Painter = (*XiaomingAsPainter)(&xm)")。這樣,小明還會被誤認為是牛仔嗎?顯然沒有這個可能性。
那麼如果小王既是畫家又是牛仔,只要讓小王同時具有牛仔和畫家的身份(類型)就可以:
type Painter interface { Draw()}type Cowboy interface { Draw()}type Xiaowang struct {}type XiaowangAsPainter Xiaowangfunc (p *XiaowangAsPainter) Draw() { fmt.Println("I'm painting.")}type XiaowangAsCowboy Xiaowangfunc (p *XiaowangAsCowboy) Draw() { fmt.Println("I'm drawing.")}func main() { var xw Xiaowang var painter Painter = (*XiaowangAsPainter)(&xw) painter.Draw() var cowboy Cowboy = (*XiaowangAsCowboy)(&xw) cowboy.Draw()}
go的實現把不同類型的行為特性區分開來,又從資料本質的層面把它們聯絡在一起(都是一個人的不同身份),這充分體現了go語言對對象和資料的理解。
通俗一點說就是定義出不同的類型,再根據這些類型分別定義相應的方法。這樣不同的類型就分別實現了不同的介面,雖然這些類型的未經處理資料可能是一樣的。這也體現了物件導向編程的思想。