欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang接口使用教程詳解

 更新時(shí)間:2022年09月30日 15:04:58   作者:Leefs  
在?Go?語(yǔ)言中接口包含兩種含義:它既是方法的集合,?同時(shí)還是一種類型并且在Go?語(yǔ)言中是隱式實(shí)現(xiàn)的。本文通過(guò)示例詳細(xì)介紹了Golang接口的使用,需要的可以參考一下

前言

go語(yǔ)言并沒(méi)有面向?qū)ο蟮南嚓P(guān)概念,go語(yǔ)言提到的接口和java、c++等語(yǔ)言提到的接口不同,它不會(huì)顯示的說(shuō)明實(shí)現(xiàn)了接口,沒(méi)有繼承、子類、implements關(guān)鍵詞。

一、概述

在 Go 語(yǔ)言中接口包含兩種含義:它既是方法的集合, 同時(shí)還是一種類型。在Go 語(yǔ)言中是隱式實(shí)現(xiàn)的,意思就是對(duì)于一個(gè)具體的類型,不需要聲明它實(shí)現(xiàn)了哪些接口,只需要提供接口所必需的方法。

go語(yǔ)言通過(guò)隱性的方式實(shí)現(xiàn)了接口功能,相對(duì)比較靈活。

Go語(yǔ)言接口的特點(diǎn)

  • interface 是方法或行為聲明的集合
  • interface接口方式實(shí)現(xiàn)比較隱性,任何類型的對(duì)象實(shí)現(xiàn)interface所包含的全部方法,則表明該類型實(shí)現(xiàn)了該接口。
  • interface還可以作為一種通用的類型,其他類型變量可以給interface聲明的變量賦值。
  • interface 可以作為一種數(shù)據(jù)類型,實(shí)現(xiàn)了該接口的任何對(duì)象都可以給對(duì)應(yīng)的接口類型變量賦值。

二、接口類型

2.1 接口的定義

每個(gè)接口類型由任意個(gè)方法簽名組成,接口的定義格式如下:

type 接口類型名 interface{
    方法名1( 參數(shù)列表1 ) 返回值列表1
    方法名2( 參數(shù)列表2 ) 返回值列表2
    …
}

說(shuō)明

  • 接口類型名:使用 type 將接口定義為自定義的類型名。Go語(yǔ)言的接口在命名時(shí),一般會(huì)在單詞后面添加 er,如有寫(xiě)操作的接口叫 Writer,有字符串功能的接口叫 Stringer,有關(guān)閉功能的接口叫 Closer 等。接口名最好要能突出該接口的類型含義。
  • 方法名:當(dāng)方法名首字母是大寫(xiě)時(shí),且這個(gè)接口類型名首字母也是大寫(xiě)時(shí),這個(gè)方法可以被接口所在的包(package)之外的代碼訪問(wèn)。
  • 參數(shù)列表、返回值列表:參數(shù)列表和返回值列表中的參數(shù)變量名可以被忽略。

舉個(gè)例子,定義一個(gè)包含Write方法的Writer接口。

type writer interface{
    Write([]byte) error
}

2.2 實(shí)現(xiàn)接口的條件

接口就是規(guī)定了一個(gè)需要實(shí)現(xiàn)的方法列表,在 Go 語(yǔ)言中一個(gè)類型只要實(shí)現(xiàn)了接口中規(guī)定的所有方法,那么我們就稱它實(shí)現(xiàn)了這個(gè)接口。

示例

定義的Eater接口類型,它包含一個(gè)Eat方法。

// Eater 接口
type Eater interface {
	Eat()
}

有一個(gè)Dog結(jié)構(gòu)體類型如下。

type Dog struct {}

因?yàn)?code>Eater接口只包含一個(gè)Eat方法,所以只需要給Dog結(jié)構(gòu)體添加一個(gè)Eat方法就可以滿足Eater接口的要求。

//Dog類型的Eat方法
func (d Dog) Eat() {
	fmt.Println("吃骨頭!")
}

這樣就稱為Dog實(shí)現(xiàn)了Eater接口。

完整代碼

// Eater 接口
type Eater interface {
	Eat()
}

type Dog struct {}

//Dog類型的Eat方法
func (d Dog) Eat() {
	fmt.Println("吃骨頭!")
}

func main() {
	dog := Dog{}
	dog.Eat()
}

2.3 為什么需要接口

多數(shù)情況下,數(shù)據(jù)可能包含不同的類型,卻會(huì)有一個(gè)或者多個(gè)共同點(diǎn),這些共同點(diǎn)就是抽象的基礎(chǔ)。

示例

// Eater 接口
type Eater interface {
	Eat()
}

type Dog struct {}

//Dog類型的Eat方法
func (d Dog) Eat() {
	fmt.Println("狗狗喜歡吃骨頭!")
}

type Cat struct {}

func (c Cat) Eat(){
	fmt.Println("小貓喜歡吃魚(yú)!")
}

func main() {
	dog := Dog{}
	dog.Eat()
	cat := Cat{}
	cat.Eat()
}

從動(dòng)物身上,可以抽象出來(lái)一個(gè)eat方法,這樣即使在擴(kuò)展其它動(dòng)物進(jìn)來(lái),也只需要實(shí)現(xiàn)Eater 接口中的Eat()方法就可以完成對(duì)這個(gè)動(dòng)作的調(diào)用。

接口可以理解為某一個(gè)方面的抽象,可以是多對(duì)一的(多個(gè)類型實(shí)現(xiàn)一個(gè)接口),這也是多態(tài)的體現(xiàn)。

2.4 接口類型變量

一個(gè)接口類型的變量能夠存儲(chǔ)所有實(shí)現(xiàn)了該接口的類型變量。

例如在上面的示例中,DogCat類型均實(shí)現(xiàn)了Eater接口,此時(shí)一個(gè)Eater類型的變量就能夠接收CatDog類型的變量。

var x Eater // 聲明一個(gè)Eater類型的變量x
a := Cat{}  // 聲明一個(gè)Cat類型變量a
b := Dog{}  // 聲明一個(gè)Dog類型變量b
x = a       // 可以把Cat類型變量直接賦值給x
x.Eat()     // 小貓喜歡吃魚(yú)!
x = b       // 可以把Dog類型變量直接賦值給x
x.Eat()     // 狗狗喜歡吃骨頭!

三、值接收者和指針接收者

通過(guò)下方一個(gè)示例來(lái)演示實(shí)現(xiàn)接口使用值接收者和使用指針接收者有什么區(qū)別。

定義一個(gè)Mover接口,它包含一個(gè)Move方法。

// Mover 定義一個(gè)接口類型
type Mover interface {
	Move()
}

3.1 值接收者實(shí)現(xiàn)接口

我們定義一個(gè)Dog結(jié)構(gòu)體類型,并使用值接收者為其定義一個(gè)Move方法。

// Dog 狗結(jié)構(gòu)體類型
type Dog struct{}

// Move 使用值接收者定義Move方法實(shí)現(xiàn)Mover接口
func (d Dog) Move() {
	fmt.Println("狗會(huì)動(dòng)")
}

此時(shí)實(shí)現(xiàn)Mover接口的是Dog類型。

var x Mover    // 聲明一個(gè)Mover類型的變量x

var d1 = Dog{} // d1是Dog類型
x = d1         // 可以將d1賦值給變量x
x.Move()

var d2 = &Dog{} // d2是Dog指針類型
x = d2          // 也可以將d2賦值給變量x
x.Move()

從上面的代碼中我們可以發(fā)現(xiàn),使用值接收者實(shí)現(xiàn)接口之后,不管是結(jié)構(gòu)體類型還是對(duì)應(yīng)的結(jié)構(gòu)體指針類型的變量都可以賦值給該接口變量。

3.2 指針接收者實(shí)現(xiàn)接口

我們?cè)賮?lái)測(cè)試一下使用指針接收者實(shí)現(xiàn)接口有什么區(qū)別。

// Cat 貓結(jié)構(gòu)體類型
type Cat struct{}

// Move 使用指針接收者定義Move方法實(shí)現(xiàn)Mover接口
func (c *Cat) Move() {
	fmt.Println("貓會(huì)動(dòng)")
}

此時(shí)實(shí)現(xiàn)Mover接口的是*Cat類型,我們可以將*Cat類型的變量直接賦值給Mover接口類型的變量x。

var c1 = &Cat{} // c1是*Cat類型
x = c1          // 可以將c1當(dāng)成Mover類型
x.Move()

但是不能給將Cat類型的變量賦值給Mover接口類型的變量x。

// 下面的代碼無(wú)法通過(guò)編譯
var c2 = Cat{} // c2是Cat類型
x = c2         // 不能將c2當(dāng)成Mover類型

由于Go語(yǔ)言中有對(duì)指針求值的語(yǔ)法糖,對(duì)于值接收者實(shí)現(xiàn)的接口,無(wú)論使用值類型還是指針類型都沒(méi)有問(wèn)題。但是我們并不總是能對(duì)一個(gè)值求址,所以對(duì)于指針接收者實(shí)現(xiàn)的接口要額外注意。

四、類型與接口的關(guān)系

4.1 一個(gè)類型實(shí)現(xiàn)多個(gè)接口

一個(gè)類型可以同時(shí)實(shí)現(xiàn)多個(gè)接口,而接口間彼此獨(dú)立,不知道對(duì)方的實(shí)現(xiàn)。

示例

動(dòng)物不僅有吃的屬性,還有動(dòng)的屬性,可以通過(guò)定義兩個(gè)接口,讓同一個(gè)動(dòng)物分別實(shí)現(xiàn)這兩種屬性

// Eater 接口
type Eater interface {
	Eat()
}

// Mover 接口
type Mover interface {
	Move()
}

type Dog struct {}

//Dog類型的Eat方法
func (d Dog) Eat() {
	fmt.Println("狗狗喜歡吃骨頭!")
}

//Dog類型的Move方法
func (d Dog) Move(){
	fmt.Println("狗狗喜歡玩耍!")
}

func main() {
	//初始化結(jié)構(gòu)體
	dog := Dog{}

	//dog實(shí)現(xiàn)了Eater和Mover兩個(gè)接口
	eat := dog
	move := dog

	eat.Eat()	//對(duì)Eater類型調(diào)用Eat方法
	move.Move()	//對(duì)Mover類型調(diào)用Move方法
}

程序中的結(jié)構(gòu)體Dog分別實(shí)現(xiàn)了Eater和Mover兩個(gè)接口中的方法。

4.2 多種類型實(shí)現(xiàn)同一接口

Go語(yǔ)言中不同的類型還可以實(shí)現(xiàn)同一接口。

一個(gè)接口的所有方法,不一定需要由一個(gè)類型完全實(shí)現(xiàn),接口的方法可以通過(guò)在類型中嵌入其他類型或者結(jié)構(gòu)體來(lái)實(shí)現(xiàn)。

// WashingMachine 洗衣機(jī)
type WashingMachine interface {
	wash()
	dry()
}

// 甩干器
type dryer struct{}

// 實(shí)現(xiàn)WashingMachine接口的dry()方法
func (d dryer) dry() {
	fmt.Println("甩一甩")
}

// 洗衣機(jī)
type haier struct {
	dryer //嵌入甩干器
}

// 實(shí)現(xiàn)WashingMachine接口的wash()方法
func (h haier) wash() {
	fmt.Println("洗刷刷")
}

func main() {
	h := haier{}
	h.dry()
	h.wash()
}

五、接口嵌套

接口與接口之間可以通過(guò)互相嵌套形成新的接口類型。例如Go標(biāo)準(zhǔn)庫(kù)io源碼中就有很多接口之間互相組合的示例。

// src/io/io.go

type Reader interface {
	Read(p []byte) (n int, err error)
}

type Writer interface {
	Write(p []byte) (n int, err error)
}

type Closer interface {
	Close() error
}

// ReadWriter 是組合Reader接口和Writer接口形成的新接口類型
type ReadWriter interface {
	Reader
	Writer
}

// ReadCloser 是組合Reader接口和Closer接口形成的新接口類型
type ReadCloser interface {
	Reader
	Closer
}

// WriteCloser 是組合Writer接口和Closer接口形成的新接口類型
type WriteCloser interface {
	Writer
	Closer
}

對(duì)于這種由多個(gè)接口類型組合形成的新接口類型,同樣只需要實(shí)現(xiàn)新接口類型中規(guī)定的所有方法就算實(shí)現(xiàn)了該接口類型。

接口也可以作為結(jié)構(gòu)體的一個(gè)字段,我們來(lái)看一段Go標(biāo)準(zhǔn)庫(kù)sort源碼中的示例。

// src/sort/sort.go

// Interface 定義通過(guò)索引對(duì)元素排序的接口類型
type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}


// reverse 結(jié)構(gòu)體中嵌入了Interface接口
type reverse struct {
    Interface
}

通過(guò)在結(jié)構(gòu)體中嵌入一個(gè)接口類型,從而讓該結(jié)構(gòu)體類型實(shí)現(xiàn)了該接口類型,并且還可以改寫(xiě)該接口的方法。

// Less 為reverse類型添加Less方法,重寫(xiě)原Interface接口類型的Less方法
func (r reverse) Less(i, j int) bool {
	return r.Interface.Less(j, i)
}

Interface類型原本的Less方法簽名為Less(i, j int) bool,此處重寫(xiě)為r.Interface.Less(j, i),即通過(guò)將索引參數(shù)交換位置實(shí)現(xiàn)反轉(zhuǎn)。

在這個(gè)示例中還有一個(gè)需要注意的地方是reverse結(jié)構(gòu)體本身是不可導(dǎo)出的(結(jié)構(gòu)體類型名稱首字母小寫(xiě)),sort.go中通過(guò)定義一個(gè)可導(dǎo)出的Reverse函數(shù)來(lái)讓使用者創(chuàng)建reverse結(jié)構(gòu)體實(shí)例。

func Reverse(data Interface) Interface {
	return &reverse{data}
}

這樣做的目的是保證得到的reverse結(jié)構(gòu)體中的Interface屬性一定不為nil,否者r.Interface.Less(j, i)就會(huì)出現(xiàn)空指針panic。

六、空接口

Golang 中的接口可以不定義任何方法,沒(méi)有定義任何方法的接口就是空接口。空接口表示沒(méi)有任何約束,因此任何類型變量都可以實(shí)現(xiàn)空接口。

空接口在實(shí)際項(xiàng)目中用的是非常多的,用空接口可以表示任意數(shù)據(jù)類型。

示例

func main() {
	//定義一個(gè)空接口x,x變量可以接收任意的數(shù)據(jù)類型
	var x interface{}
	str := "Hello Go"
	x = str
	fmt.Printf("type:%T,value:%v\n",x,x)

	num := 10
	x = num
	fmt.Printf("type:%T,value:%v\n",x,x)

	bool := true
	x = bool
	fmt.Printf("type:%T,value:%v\n",x,x)
}

運(yùn)行結(jié)果

type:string,value:Hello Go
type:int,value:10
type:bool,value:true

1、空接口作為函數(shù)的參數(shù)

// 空接口作為函數(shù)參數(shù)
func show(a interface{}) {
	fmt.Printf("type:%T value:%v\n", a, a)
}

func main() {
	show(1)
	show(true)
	show(3.14)
	var mapStr = make(map[string]string)
	mapStr["name"] = "Leefs"
	mapStr["age"] = "12"
	show(mapStr)
}

運(yùn)行結(jié)果

type:int value:1
type:bool value:true
type:float64 value:3.14
type:map[string]string value:map[age:12 name:Leefs]

2、map的值實(shí)現(xiàn)空接口

func main() {
	// 空接口作為 map 值
	var studentInfo = make(map[string]interface{})
	studentInfo["name"] = "Jeyoo"
	studentInfo["age"] = 18
	studentInfo["married"] = false
	fmt.Println(studentInfo)
}

運(yùn)行結(jié)果

map[age:18 married:false name:Jeyoo]

3、切片實(shí)現(xiàn)空接口

func main() {
	var slice = []interface{}{"Jeyoo", 20, true, 32.2}
	fmt.Println(slice)
}

運(yùn)行結(jié)果

[Jeyoo 20 true 32.2]

七、類型斷言

一個(gè)接口的值(簡(jiǎn)稱接口值)是由一個(gè)具體類型和具體類型的值兩部分組成的。這兩部分分別稱為接口的動(dòng)態(tài)類型和動(dòng)態(tài)值。

如果我們想要判斷空接口中值的類型,那么這個(gè)時(shí)候就可以使用類型斷言,其語(yǔ)法格式:

x.(T)

說(shuō)明

  • x: 表示類型為 interface{}的變量
  • T: 表示斷言 x 可能是的類型

該語(yǔ)法返回兩個(gè)參數(shù),第一個(gè)參數(shù)是 x 轉(zhuǎn)化為 T 類型后的變量,第二個(gè)值是一個(gè)布爾值,若為 true 則表示斷言成功,為 false 則表示斷言失敗。

示例

func main() {
	var x interface{}
	x = "Hello GO"
	v, ok := x.(string)
	if ok {
		fmt.Println(v)
	} else {
		fmt.Println("類型斷言失敗")
	}
}

上面的示例中如果要斷言多次就需要寫(xiě)多個(gè) if 判斷,這個(gè)時(shí)候我們可以使用 switch 語(yǔ)句來(lái) 實(shí)現(xiàn):

注意:類型.(type)只能結(jié)合 switch 語(yǔ)句使用

// justifyType 對(duì)傳入的空接口類型變量x進(jìn)行類型斷言
func justifyType(x interface{}) {
	switch v := x.(type) {
	case string:
		fmt.Printf("x is a string,value is %v\n", v)
	case int:
		fmt.Printf("x is a int is %v\n", v)
	case bool:
		fmt.Printf("x is a bool is %v\n", v)
	default:
		fmt.Println("unsupport type!")
	}
}

由于接口類型變量能夠動(dòng)態(tài)存儲(chǔ)不同類型值的特點(diǎn),所以很多初學(xué)者會(huì)濫用接口類型(特別是空接口)來(lái)實(shí)現(xiàn)編碼過(guò)程中的便捷。

只有當(dāng)有兩個(gè)或兩個(gè)以上的具體類型必須以相同的方式進(jìn)行處理時(shí)才需要定義接口。切記不要為了使用接口類型而增加不必要的抽象,導(dǎo)致不必要的運(yùn)行時(shí)損耗。

總結(jié)

在 Go 語(yǔ)言中接口是一個(gè)非常重要的概念和特性,使用接口類型能夠?qū)崿F(xiàn)代碼的抽象和解耦,也可以隱藏某個(gè)功能的內(nèi)部實(shí)現(xiàn),但是缺點(diǎn)就是在查看源碼的時(shí)候,不太方便查找到具體實(shí)現(xiàn)接口的類型。

相信很多讀者在剛接觸到接口類型時(shí)都會(huì)有很多疑惑,請(qǐng)牢記接口是一種類型,一種抽象的類型。區(qū)別于我們?cè)谥罢鹿?jié)提到的那些具體類型(整型、數(shù)組、結(jié)構(gòu)體類型等),它是一個(gè)只要求實(shí)現(xiàn)特定方法的抽象類型。

以上就是Golang接口使用教程詳解的詳細(xì)內(nèi)容,更多關(guān)于Golang接口的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語(yǔ)言基礎(chǔ)學(xué)習(xí)教程

    Go語(yǔ)言基礎(chǔ)學(xué)習(xí)教程

    這篇文章主要介紹了Go語(yǔ)言基礎(chǔ)知識(shí),包括基本語(yǔ)法、語(yǔ)句、數(shù)組等的定義與用法,需要的朋友可以參考下
    2016-07-07
  • Go語(yǔ)言中的函數(shù)詳解

    Go語(yǔ)言中的函數(shù)詳解

    函數(shù)是基本的代碼塊,用于執(zhí)行一個(gè)任務(wù)。本文詳細(xì)講解了Go語(yǔ)言中的函數(shù),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • GO 反射對(duì)性能的影響分析

    GO 反射對(duì)性能的影響分析

    這篇文章主要為大家介紹了GO 反射對(duì)性能的影響分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • golang中bufio.SplitFunc的深入理解

    golang中bufio.SplitFunc的深入理解

    這篇文章主要給大家介紹了關(guān)于golang中bufio.SplitFunc的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用golang具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • 解決go echo后端處理跨域的兩種操作方式

    解決go echo后端處理跨域的兩種操作方式

    這篇文章主要介紹了解決go echo后端處理跨域的兩種操作方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Go語(yǔ)言壓縮和解壓縮tar.gz文件的方法

    Go語(yǔ)言壓縮和解壓縮tar.gz文件的方法

    這篇文章主要介紹了Go語(yǔ)言壓縮和解壓縮tar.gz文件的方法,實(shí)例分析了使用Go語(yǔ)言壓縮文件與解壓文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Golang如何將上傳的文件壓縮成zip(小案例)

    Golang如何將上傳的文件壓縮成zip(小案例)

    這篇文章主要介紹了Golang如何將上傳的文件壓縮成zip(小案例),這是一個(gè)簡(jiǎn)單的golang壓縮文件小案例,可做很多的拓展,這里使用的庫(kù)是archive/zip,在gopkg里面搜zip就行,需要的朋友可以參考下
    2024-01-01
  • 使用Go語(yǔ)言實(shí)現(xiàn)遠(yuǎn)程傳輸文件

    使用Go語(yǔ)言實(shí)現(xiàn)遠(yuǎn)程傳輸文件

    本文主要介紹如何利用Go語(yǔ)言實(shí)現(xiàn)遠(yuǎn)程傳輸文件的功能,有需要的小伙伴們可以參考學(xué)習(xí)。下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)。
    2016-08-08
  • Golang中slice切片的實(shí)現(xiàn)示例

    Golang中slice切片的實(shí)現(xiàn)示例

    Go語(yǔ)言中,切片是對(duì)數(shù)組的抽象,提供了更靈活的動(dòng)態(tài)數(shù)組解決方案,本文就來(lái)介紹一下Golang中slice切片的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-09-09
  • 深入了解Golang官方container/list原理

    深入了解Golang官方container/list原理

    在?Golang?的標(biāo)準(zhǔn)庫(kù)?container?中,包含了幾種常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn),其實(shí)是非常好的學(xué)習(xí)材料,本文主要為大家介紹了container/list的原理與使用,感興趣的可以了解一下
    2023-08-08

最新評(píng)論