Go入門22:接口 interface

接口簡介

Go 語言不是一種“傳統(tǒng)” 的面向對象編程語言, 所以 Go 語言并沒有類和繼承的概念。但是 Go 語言里有非常靈活的接口概念,通過它可以實現(xiàn)很多面向對象的特性。

在Go語言的實際編程中,幾乎所有的數(shù)據(jù)結構都圍繞接口展開,接口是Go語言中所有數(shù)據(jù)結構的核心。

接口是一種契約,實現(xiàn)類型必須滿足它,它描述了類型的行為,規(guī)定類型可以做什么。接口徹底將類型能做什么,以及如何做分離開來,使得相同接口的變量在不同的時刻表現(xiàn)出不同的行為。

Go語言中的接口是一些方法的集合(method set),它指定了對象的行為:如果它(任何數(shù)據(jù)類型)可以做這些事情,那么它就可以在這里使用??匆环N類型是不是“實現(xiàn)”了一個接口,就得看這種類型是不是實現(xiàn)了接口中定義的所有方法。

什么是interface

簡單的說,interface是一組method簽名的組合,我們通過interface來定義對象的一組行為。

接口定義中不能包含變量。

/* 定義接口 */

type interface_name interface {

? method_name1 [return_type]

? method_name2 [return_type]

? method_name3 [return_type]

? ...

? method_namen [return_type]

}

/* 定義結構體 */

type struct_name struct {?

????/* variables */

}

/* 實現(xiàn)接口方法 */

func (struct_name_variable struct_name) method_name1() [return_type] {?

????/* 方法實現(xiàn) */

}

...

func (struct_name_variable struct_name) method_namen() [return_type] {?

????/* 方法實現(xiàn)*/

}

interface類型

interface類型定義了一組方法,如果某個對象實現(xiàn)了某個接口的所有方法,則此對象就實現(xiàn)了此接口。

在定義了一個接口之后,一般使用一個自定義結構體(struct)去實現(xiàn)接口中的方法。

interface可以被任意的對象實現(xiàn),一個對象可以實現(xiàn)任意多個interface!

對于某個接口的同一個方法,不同的結構體(struct)可以有不同的實現(xiàn),例如:

type Phone interface {

? ? call()

}

type NokiaPhone struct {}

type IPhone struct {}

func (nokiaPhone NokiaPhone) call() {

? ? fmt.Println("I am Nokia, I can call you!")

}

func (iPhone IPhone) call() {

? ? fmt.Println("I am iPhone, I can call you!")

}

func main() {

? ? var phone Phone

? ? phone = new(NokiaPhone)

? ? phone.call()

? ? phone = new(IPhone)

? ? phone.call()

}

interface值

不像大多數(shù)面向對象編程語言,在 Go 語言中接口可以有值。假設有個接口定義如下:

type Namer interface {

? ? Method1(param_list) return_type

? ? Method2(param_list) return_type

? ? ...

}

那么定義var ai Namer中,ai是有值的,只不過這個時候,ai是接口的“零值”狀態(tài),值是 nil。

事實上 ,Go 語言中接口不僅有值,還能進行接口賦值,接口賦值分為以下兩種情況:

1. 將對象實例賦值給接口。

2. 將一個接口賦值給另一個接口。

其中:

將對象實例賦值給接口要求對象實現(xiàn)了接口的所有方法;

接口之間的賦值要求接口A中定義的所有方法,都在接口B中有定義,那么B接口的實例可以賦值給A的對象。反之不一定成立,除非A和B定義的方法完全一樣(順序不要求),這時A和B等價,可以相互賦值。

示例代碼如下:

type Shaper interface {

? ? Area() float32

}

type Square struct {

? ? side float32

}

func (sq *Square) Area() float32 {

? ? return sq.side * sq.side

}

func main() {

? ? sq1 := new(Square)

? ? sq1.side = 5

? ? // 賦值方法1:

? ? //var areaIntf Shaper

? ? // areaIntf = sq1

? ? // 更短的賦值方法2:

? ? // areaIntf := Shaper(sq1)

? ? // 最簡潔的賦值方法3:

? ? areaIntf := sq1

? ? fmt.Printf("The square has area: %f\n", areaIntf.Area())

}

上面的程序定義了一個結構體 Square 和一個接口 Shaper,接口有一個方法 Area()。

在main() 方法中創(chuàng)建了一個Square的實例。在主程序外邊定義了一個接收者類型是 Square 方法的 Area(),用來計算正方形的面積:結構體 Square 實現(xiàn)了接口 Shaper 。

所以可以將一個 Square類型的變量賦值給一個接口類型的變量:areaIntf = sq1 。

現(xiàn)在接口變量包含一個指向Square 變量的引用,通過 areaIntf 可以調用Square上的方法 Area()。

空interface

空interface(interface{})不包含任何的method,所有的類型都實現(xiàn)了空interface。

空interface對于描述起不到任何的作用(因為它不包含任何的method),但是空interface在我們需要存儲任意類型的數(shù)值的時候相當有用,因為它可以存儲任意類型的數(shù)值。

//定義a為空接口

var a interface{}

var i int = 5

s := "Hello world"

//a可以存儲任意類型的數(shù)值

a = i

a = s

一個函數(shù)把interface{}作為參數(shù),那么他可以接受任意類型的值作為參數(shù),如果一個函數(shù)返回interface{},那么也就可以返回任意類型的值。

interface函數(shù)參數(shù)

interface的變量可以持有任意實現(xiàn)該interface類型的對象,我們可以通過定義interface參數(shù),讓函數(shù)接受各種類型的參數(shù)。

interface存儲變量的類型

interface的變量里面可以存儲任意類型的數(shù)值,那么我們怎么反向知道這個變量里面實際保存了的是哪個類型的對象呢?

1、Comma-ok斷言

value, ok = element.(T)

value就是變量的值,ok是一個bool類型,element是interface變量,T是斷言的類型。

如果element里面確實存儲了T類型的數(shù)值,那么ok返回true,否則返回false。

import (

????"fmt"

????"strconv"

)

type Element interface{}

type List []Element

type Person struct {

????name string

????age? int

}

// 定義了String方法,實現(xiàn)了fmt.Stringer

func (p Person) String() string {

????return "(name: " + p.name + " = age: " + strconv.Itoa(p.age) + " years)"

}

func main() {

????list := make(List, 3)

????list[0] = 1

????list[1] = "Hello"

????list[2] = Person{"Jeny", 70}

????for index, element := range list {

????????if value, ok := element.(int); ok {

????????????fmt.Printf("list[%d] is an int and its value is %d\n", index, value)

????????} else if value, ok := element.(string); ok {

????????????fmt.Printf("list[%d] is an int and its value is %s\n", index, value)

????????} else if value, ok := element.(Person); ok {

????????????fmt.Printf("list[%d] is an int and its value is %s\n", index, value)

????????} else {

????????????fmt.Printf("list[%d] is of a different type\n", index)

????????}

????}

}

2、switch測試

func main() {

????list := make(List, 3)

????list[0] = 1

????list[1] = "Hello"

????list[2] = Person{"Jeny", 70}

????for index, element := range list {

????????switch value := element.(type) {

????????case int:

????????????fmt.Printf("list[%d] is an int and its value is %d\n", index, value)

????????case string:

????????????fmt.Printf("list[%d] is an int and its value is %s\n", index, value)

????????case Person:

????????????fmt.Printf("list[%d] is an int and its value is %s\n", index, value)

????????default:

????????????fmt.Printf("list[%d] is of a different type\n", index)

????????}

????}

}

注意:`element.(type)`語法不能在switch外的任何邏輯里面使用,如果你要在switch外面判斷一個類型就使用`comma-ok`。

嵌入interface

源碼包container/heap里面有這樣的一個定義:

type Interface interface {

? ? sort.Interface ? ?// 嵌入字段sort.Interface

? ? Push(x interface{})

? ? Pop() interface{}

}

sort.Interface其實就是嵌入字段,把sort.Interface的所有method給隱式的包含進來了。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容