接口(interface)定義了一個(gè)對(duì)象的行為規(guī)范,只定義規(guī)范不實(shí)現(xiàn),由具體的對(duì)象來(lái)實(shí)現(xiàn)規(guī)范的細(xì)節(jié)。

在Go語(yǔ)言中接口(interface)是一種類型,是一種抽象的類型。
interface是一組method的集合,是duck-type programming的一種體現(xiàn)。接口做的事情就像是定義一個(gè)協(xié)議(規(guī)則),只要一臺(tái)機(jī)器有洗衣服和甩干的功能,我就稱它為洗衣機(jī)。不關(guān)心屬性(數(shù)據(jù)),只關(guān)心行為(方法)。
為了保護(hù)你的Go語(yǔ)言職業(yè)生涯,請(qǐng)牢記接口(interface)是一種類型。
package main
import (
"fmt"
)
type Cat struct{}
func (c Cat)Say() string{
return "喵喵喵"
}
type Dog struct{}
func (d Dog)Say() string{
return "汪汪汪"
}
func main(){
c := Cat{}
fmt.Println("貓:",c.Say())
d := Dog{}
fmt.Println("狗:",d.Say())
}上面的代碼中定義了貓和狗,然后它們都會(huì)叫,你會(huì)發(fā)現(xiàn)main函數(shù)中明顯有重復(fù)的代碼,如果我們后續(xù)再加上豬、青蛙等動(dòng)物的話,我們的代碼還會(huì)一直重復(fù)下去。那我們能不能把它們當(dāng)成“能叫的動(dòng)物”來(lái)處理呢?
類似的例子在我們編程的過(guò)程中會(huì)經(jīng)常遇到:
比如一個(gè)網(wǎng)上商城可能使用支付寶、微信、銀聯(lián)等方式去在線支付,我們能不能把它們當(dāng)成“支付方式”來(lái)處理呢?
比如三角形、四邊形、圓形都能計(jì)算周長(zhǎng)和面積,我們能不能把它們都當(dāng)成“圖形”來(lái)處理呢?
比如銷售、行政、程序員都能計(jì)算月薪,我們能不能把它們都當(dāng)成“員工”來(lái)處理呢?
Go語(yǔ)言中為了解決類似上面的問(wèn)題,就設(shè)計(jì)了接口這個(gè)概念。接口區(qū)別于我們之前所有的具體類型,接口是一種抽象的類型,當(dāng)你看到一個(gè)接口類型的值時(shí),你不知道它是什么,唯一知道的是通過(guò)它的方法能做什么。
Go語(yǔ)言提倡面向接口編程。
每個(gè)接口由數(shù)個(gè)方法組成,接口的定義格式如下:
type 接口類型名 interface {
方法名1(參數(shù)列表1) 返回值列表1
方法名2(參數(shù)列表2) 返回值列表2
...
}其中:
舉個(gè)例子:
type writer interface{
write([]byte) err
}當(dāng)你看到這個(gè)接口類型的值時(shí),你不知道它是什么,但是你唯一知道的就是可以通過(guò)它的Write方法來(lái)做一些事情。
一個(gè)對(duì)象只要全部實(shí)現(xiàn)了接口里面定義的方法,那么他就是現(xiàn)實(shí)了這個(gè)接口。我們來(lái)定義一個(gè)Animal接口:
type Sayer interface{
say()
}定義dog和cat兩個(gè)結(jié)構(gòu)體
type dog struct{}
type cat struct{}因?yàn)镾ayer接口里只有一個(gè)say方法,所以我們只需要給dog和cat分別實(shí)現(xiàn)say方法就可以實(shí)現(xiàn)Sayer接口了。
//dog實(shí)現(xiàn)了Sayer接口
func (d dog) say(){
fmt.Println("汪汪汪")
}
//cat實(shí)現(xiàn)了Sayer接口
func (c cat) say(){
fmt.Println("喵喵喵")
}接口的實(shí)現(xiàn)就是這么簡(jiǎn)單,只要實(shí)現(xiàn)了接口中的所有方法,就實(shí)現(xiàn)了這個(gè)接口。
那實(shí)現(xiàn)了接口由什么用呢?
接口類型變量能夠存儲(chǔ)所有實(shí)現(xiàn)了該接口的實(shí)例,例如上面的示例中,Sayer類型的變量能夠存儲(chǔ)dog和cat類型的變量。
func main(){
var x Sayer
a := cat{}
b := dog{}
x =a
x.say()
x = b
x.say()
}
使用值接收者實(shí)現(xiàn)接口和使用指針接收者實(shí)現(xiàn)接口有什么區(qū)別呢?接下來(lái)我們通過(guò)一個(gè)例子看一下其中的區(qū)別。
我們有一個(gè)Mover接口和一個(gè)dog結(jié)構(gòu)體。
type Mover interface{
move()
}
type dog struct{}值接收者實(shí)現(xiàn)接口
func (d dog)move(){
fmt.Println("狗會(huì)動(dòng)")
}此時(shí)實(shí)現(xiàn)接口的是dog 類型:
func main(){
var x Mover
var wangcai = dog{}
x = wangcai
var fugui = &dog{}
x = fugui
x.move()
}從上面的代碼中我們可以發(fā)現(xiàn),使用值接收者實(shí)現(xiàn)接口之后,不管是dog結(jié)構(gòu)體還是結(jié)構(gòu)體指針dog類型的變量都可以賦值給該接口變量。因?yàn)镚o語(yǔ)言中有對(duì)指針類型變量求值的語(yǔ)法糖,dog指針fugui內(nèi)部會(huì)自動(dòng)求值fuzhi。
同樣的代碼我們?cè)賮?lái)測(cè)試一下使用指針接收者有什么區(qū)別:
func(d *dog)move(){
fmt.Println("狗會(huì)動(dòng)")
}
func main(){
var x Mover
var wangcai = dog{}
x = wangcai
var fugui = &dog{}
x = fugui
}此時(shí)實(shí)現(xiàn)Mover接口的是*dog類型,所以不能給x傳入dog類型的wangcai, 此時(shí)x只能存儲(chǔ) **dog類型的值。
一個(gè)類型可以同時(shí)實(shí)現(xiàn)多個(gè)接口,而接口間彼此獨(dú)立,不知道對(duì)方的實(shí)現(xiàn)。例如,狗可以叫,也可以動(dòng),我們就分別定義Sayer接口和Mover接口,如下: Mover接口。
type Sayer interface{
say()
}
type Mover interface{
move()
}dog既可以實(shí)現(xiàn)Sayer接口,也可以實(shí)現(xiàn)Mover接口。
type Sayer interface{
say()
}
type Mover interface{
move()
}
type dog struct{
name string
}
func (d dog)say(){
fmt.Printf("%s會(huì)叫汪汪汪\n",d.name)
}
func (d dog)move(){
fmt.Printf("%s會(huì)動(dòng)\n",d.name)
}
func main(){
var x Sayer
var y Mover
var a = dog{
name:"旺財(cái)"
}
x = a
y = a
x.say()
y.move()
}多個(gè)類型實(shí)現(xiàn)同一個(gè)接口
GO語(yǔ)言中不同的類型還可以實(shí)現(xiàn)同一個(gè)接口,首先我們定義一個(gè)Mover接口,它必須由一個(gè)move方法。
type dog struct{
name string
}
type car struct{
brand string
}
func (d dog)move(){
fmt.Printf("%s會(huì)跑\n",d.name)
}
func (c car)move(){
fmt.Printf("%s速度70邁\n",c.brand)
}
func main(){
var x Mover
var a = dog{name:"旺財(cái)"}
var b = car{brand:"寶馬"}
x = a
x.move()
x = b
x.move()
}上面的帶啊執(zhí)行結(jié)果如下:
旺財(cái)會(huì)跑
寶馬速度70邁并且一個(gè)接口方法,不一定需要一個(gè)類型完全實(shí)現(xiàn),接口的方法可以通過(guò)在類型中嵌入其他類型或者結(jié)構(gòu)體來(lái)實(shí)現(xiàn)。
type washingmachine interface{
wash()
dry()
}
type dryer struct{}
func (d dryer) dry(){
fmt.Println("甩一甩")
}
type haier struct{
dryer
}
func (h haier)wash(){
fmt.Println("洗刷刷")
}
接口與接口間可以通過(guò)嵌套創(chuàng)造出新的接口。
type Sayer interface{
say()
}
type Mover interface{
move()
}
type animal interface{
Sayer
Mover
}嵌套得到的接口的使用和普通接口一樣,這里我們讓cat實(shí)現(xiàn)animal接口:
type Sayer interface{
say()
}
type Mover interface{
move()
}
type animal interface{
Sayer
Mover
}
type cat struct {
name string
}
func (c cat)say(){
fmt.println("喵喵喵")
}
func (c cat)move(){
fmt.Println("貓會(huì)動(dòng)")
}
func main(){
var x animal
x = cat{name:"花花"}
x.move()
x.say()
}
空接口類型的變量可以存儲(chǔ)任何類型的變量。
func main(){
var x interface{}
s := "hello 沙河"
x = s
fmt.Printf("type:%T value:%v\n",x,x)
i :=100
x = i
fmt.Printf("type:%T value:%v\n", x,x)
b := true
x = b
fmt.Printf("type:%T value:%v\n", x,x )
}空接口作為函數(shù)的參數(shù)
使用空接口實(shí)現(xiàn)可以接收任意類型的函數(shù)參數(shù)。
func show(a interface{}){
fmt.Printf("type:%T value:%v\n", a,a)
}空接口作為map的值
使用空接口實(shí)現(xiàn)可以保存任意值的字典。
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "沙河娜扎"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)空接口可以存儲(chǔ)任意類型的值,那我們?nèi)绾潍@取其存儲(chǔ)的具體數(shù)據(jù)呢?
一個(gè)接口的值(簡(jiǎn)稱接口值)是由 “一個(gè)具體類型” 和 “具體類型的值” 兩部分組成的。這兩部分分別稱為接口的 “動(dòng)態(tài)類型”和“動(dòng)態(tài)值”。
我們來(lái)看一個(gè)具體的例子:
var w io.Writer
w = os.Stdout
w = new(bytes.Buffer)
w = nil想要判斷空接口的值這個(gè)時(shí)候就可以使用類型斷言,其語(yǔ)法格式為:
x.(T)其中:
x 表示類型為interface{}的變量。
T表示斷言x可能是的類型。
該語(yǔ)法返回兩個(gè)參數(shù),第一個(gè)參數(shù)是x轉(zhuǎn)化為T類型后的變量,第二個(gè)值是一個(gè)布爾值,若為true則表示斷言成功,若為false則表示斷言失敗。
舉個(gè)例子:
func main(){
var x interface{}
x = "hello 沙河"
v,ok := x.(string)
if ok {
fmt.Println(v)
}else {
fmt.Println("類型斷言失敗")
}
}上面的示例中如果需要斷言多次就需要寫多個(gè)if判斷,這個(gè)時(shí)候我們可以使用switch語(yǔ)句來(lái)實(shí)現(xià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,value is %v\n",v)
case bool:
fmt.Printf("x is a bool, value is %v\n",v)
default:
fmt.Printf("unsupport type!")
}
}因?yàn)榭战涌诳梢源鎯?chǔ)任意類型值的特點(diǎn),所以空接口在Go語(yǔ)言中的使用十分廣泛。
關(guān)于接口需要注意的是,只有當(dāng)有兩個(gè)或者兩個(gè)以上的具體類型必須以相同的方式進(jìn)行處理時(shí)才需要定義接口。不要為了接口而寫接口。那樣只會(huì)增加不必要的抽象,導(dǎo)致不必要的運(yùn)行時(shí)損耗。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
網(wǎng)頁(yè)標(biāo)題:go語(yǔ)言碎片整理之接口-創(chuàng)新互聯(lián)
文章路徑:http://chinadenli.net/article0/goiio.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、自適應(yīng)網(wǎng)站、Google、微信小程序、網(wǎng)站策劃、軟件開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容