golang接口看這篇就夠啦

interface

interface是go中一個非常重要的概念,使用的地方非常非常多瘾英,有必要好好學習。

  • 那什么是接口呢做葵?
    在現(xiàn)實中我們有usb接口护蝶、type-c接口华烟、HDMI接口,它們是一種約定,凡是實現(xiàn)了這個約定的電源線,插上就能有對應(yīng)的功能;

回到go中,go的接口也類似這樣,它是一種約定持灰,約定實現(xiàn)了xx方法盔夜,那么就實現(xiàn)了該接口

  • 接口有什么用呢堤魁?
    一句話表述:可以實現(xiàn)多態(tài)和靈活的數(shù)據(jù)類型設(shè)計喂链、代碼接耦

1. 接口定義

接口定義非常簡單

type 接口名稱 interface {
  方法簽名
}

2. 多態(tài)實現(xiàn)

go是一個強類型語言, 有了接口妥泉,我們可以把具有相同功能的對象抽象為一個接口椭微,從而實現(xiàn)多態(tài)。

package main

import "fmt"

// 定義一個Speaker接口
type Speaker interface {
    // 這個接口中需要包含一個say方法
    say()
}

type Dog struct{}

// dog實現(xiàn)了Speaker接口
func (d Dog) say() {
    fmt.Println("汪汪汪盲链!")
}

type Cat struct{}

// cat實現(xiàn)了Speaker接口
func (c Cat) say() {
    fmt.Println("喵喵喵")
}

// 對Speaker切片元素調(diào)用say方法
func Speak(s []Speaker) {
    for _, sp := range s {
        sp.say() // 多態(tài)體現(xiàn)
    }
}

func main() {
    // 初始化一個長度為2的 Speaker接口類型的切片
    s := make([]Speaker, 2)

    s[0] = Cat{}
    s[1] = Dog{}

    Speak(s)
}

// 喵喵喵
// 汪汪汪蝇率!

3. 解耦合實現(xiàn)

假設(shè)在我們的項目中需要支持多種支付方式,比如微信支付刽沾、支付寶支付本慕、銀行卡支付,該如何實現(xiàn)呢悠轩?

package main

// 支付方式
type PaymentMethod interface {
    // 支付
    Pay(amount float32) error
    // 退款
    Refund(amount float32) error
}

// 支付寶支付
type AliPay struct{}

func (a *AliPay) Pay(amount float32) error {
    // 支付操作
    return nil
}
func (a *AliPay) Refund(amount float32) error {
    // 支付操作
    return nil
}

// 微信支付
type WeixinPay struct{}

func (a *WeixinPay) Pay(amount float32) error {
    // 支付操作
    return nil
}

func (a *WeixinPay) Refund(amount float32) error {
    // 支付操作
    return nil
}

// 操作支付
func DoPay(pm PaymentMethod, amount float32) {
    // 實現(xiàn)于具體支付的接耦
    pm.Pay(amount)
}

func main() {
    pm := &AliPay{}
    DoPay(pm, 100)
}

4. 值接收者 和 指針接收者

這是一個易錯點,需要特別注意哦攻泼!

在結(jié)構(gòu)體中火架,我們知道一個實例不區(qū)分值接收者還是指針接收者鉴象,都可以正常調(diào)用;但是在接口中不是這樣的何鸡,讓我們看下代碼:

package main

import "fmt"

// 薪水計算接口
type SalaryCalculator interface {
    calSalary() int
}

// 永久員工
type Permant struct {
    empId       int
    basicPay    int // 基本薪資
    performance int // 績效
}

// 實現(xiàn)薪水計算接口
func (p *Permant) calSalary() int {
    return p.basicPay + p.performance
}

func main() {
    p := Permant{empId: 1, basicPay: 100, performance: 50}

    // 定義接口類型變量
    var sc SalaryCalculator
    sc = p // 發(fā)生錯誤
    //  cannot use p (variable of type Permant) as SalaryCalculator value in assignment: Permant does not implement SalaryCalculator (method calSalary has pointer receiver)
    fmt.Println(sc.calSalary())
}

在上面的代碼中,由于接口中方法實現(xiàn)接收者是指針,所以必須保持接口對象也是指針纺弊,否則會報錯。

記住下面的規(guī)則就行:對于接口而言骡男,當方法是指針接收者時淆游,必須和接收者保持一致(指針),值接收者無所謂

為什么會這樣呢隔盛?
大概是編譯器在處理接口類型值轉(zhuǎn)指針時,轉(zhuǎn)換不了犹菱。

5. 空接口使用

空接口在go中的使用也是相當?shù)亩?主要用做任意類型值的做為參數(shù)傳入的場景。

interface {}由于空接口中沒有任何方法簽名吮炕,所以任何類型都實現(xiàn)了空接口.這也是為什么空接口可以支持任意類型參數(shù)傳入的原因腊脱。

先來看一個示例

package main

import "fmt"

// 打印任何類型的東西
func printAny(v interface{}) {
    fmt.Printf("%#v\n", v)
}

type Person struct {
    name string
}

func main() {
    // 任意類型隨便裝
    printAny("abc")
    printAny(12)
    printAny(23.54)
    printAny(Person{name: "sd"})
}

// "abc"
// 12
// 23.54
// main.Person{name:"sd"}

6. 類型斷言

在上面小結(jié)的代碼中,我們胡亂的往里面?zhèn)魅我忸愋偷闹?雖然代碼能運行,但是沒啥意義龙亲。

在真實的場景中陕凹,我們通常需要知道傳入的是什么數(shù)據(jù)類型,并對特定類型做相應(yīng)的處理鳄炉,這時候類型斷言就派上用場了杜耙。

類型斷言主要有兩種寫法:

  1. x.(T) // x是T類型,如果不是拂盯,panic
  2. val, ok := x.(T) // 比較安全如果不是 ok為false

我們實踐下

package main

import "fmt"

func main() {
    var a interface{}

    a = "hello"             // a當前是一個字符串類型
    fmt.Println(a.(string)) // ok
    // fmt.Println(a.(int))    // not ok 直接報錯

    val, ok := a.(string)
    fmt.Printf("val: %#v, ok: %v\n", val, ok)
}

// hello
// val: "hello", ok: true

如果類型很多,上面的ok模式就不太好用,go還提供了一種switch方式佑女,代碼如下:

package main

import "fmt"

func main() {
    printAny("abc")
    printAny(float32(1.23))
    printAny(23)
    printAny([]int{1, 2})
}

func printAny(v interface{}) {
    // 注意這里寫的是type
    switch val := v.(type) {
    case string:
        // 區(qū)分出類型后 再轉(zhuǎn)換成對應(yīng)類型
        fmt.Printf("這是一個字符串: %s\n", val)
    case float32:
        fmt.Printf("這是一個32位浮點數(shù): %v\n", val)
    case int:
        fmt.Printf("這是一個整型:%v\n", val)
    default:
        fmt.Printf("Unknown type: %v\n", val)
    }
}

// 這是一個字符串: abc
// 這是一個32位浮點數(shù): 1.23
// 這是一個整型:23
// Unknown type: [1 2]

6. 接口本質(zhì)

接口底層本質(zhì)是(type,value) 它們是一對的關(guān)系,任何一個接口都是由類型和值組成磕仅。

7. 空接口與nil值

接口相關(guān)的nil值和空指針結(jié)構(gòu)體比較非常容易讓人困惑珊豹,我們先來看一段代碼

package main

import "fmt"

type Person struct{}

func main() {
    var a interface{} // 空接口

    if a == nil {
        fmt.Println("空接口等于nil的哦!")
    }

    var p *Person // 一個指向Person結(jié)構(gòu)體指針
    if p == nil {
        fmt.Println("結(jié)構(gòu)體空指針也等于nil")
    }

    //========= 上面很好理解榕订,關(guān)鍵看下面 ========//
    var b interface{} // 定義一個空接口
    b = p             // 把結(jié)構(gòu)體空指針 賦給空接口

    if b != nil {
        fmt.Println("空結(jié)構(gòu)體指針接口不等于nil")
    }

    if b != a {
        fmt.Println("空結(jié)構(gòu)體指針結(jié)構(gòu)體也不等于空接口")
    }
}

// 空接口等于nil的哦店茶!
// 結(jié)構(gòu)體空指針也等于nil
// 空結(jié)構(gòu)體指針接口不等于nil
// 空結(jié)構(gòu)體指針結(jié)構(gòu)體也不等于空接口

怎么樣,對輸出有疑惑吧劫恒!我們大概總結(jié)下贩幻,
首先,無論對于空接口還是空結(jié)構(gòu)體指針它們都是 == nil
但是,空結(jié)構(gòu)體指針接口两嘴,既不等于nil, 也不等于空接口

對這個問題困惑的原因丛楚,是因為對接口本質(zhì)不夠理解,接口的內(nèi)部本質(zhì)上是由(type,value)一對組成的憔辫;

對于空接口趣些,它的類型和值都是nil;
對于空結(jié)構(gòu)體指針接口,它的類型是對應(yīng)結(jié)構(gòu)體指針類型贰您,值是nil;

所以很容易得出它們不相等坏平。

6. 接口嵌套(組合)

前面我們看到的都是單一接口拢操,接口是支持組合嵌套的,可以由多個單一接口組合成一個其它接口,比如:

// 定義基本接口
type Reader interface {
    Read() string
}

// 在基本接口的基礎(chǔ)上定義擴展接口
type ReaderWithInfo interface {
    Reader // 這里嵌套了另外一個接口
    Info() string
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舶替,一起剝皮案震驚了整個濱河市令境,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌顾瞪,老刑警劉巖舔庶,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異陈醒,居然都是意外死亡惕橙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門孵延,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吕漂,“玉大人,你說我怎么就攤上這事尘应』棠” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵犬钢,是天一觀的道長苍鲜。 經(jīng)常有香客問我,道長玷犹,這世上最難降的妖魔是什么混滔? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮歹颓,結(jié)果婚禮上坯屿,老公的妹妹穿的比我還像新娘。我一直安慰自己巍扛,他們只是感情好领跛,可當我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撤奸,像睡著了一般吠昭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胧瓜,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天矢棚,我揣著相機與錄音,去河邊找鬼府喳。 笑死蒲肋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兜粘,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼强胰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了妹沙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤熟吏,失蹤者是張志新(化名)和其女友劉穎距糖,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牵寺,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡悍引,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帽氓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趣斤。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖黎休,靈堂內(nèi)的尸體忽然破棺而出浓领,到底是詐尸還是另有隱情,我是刑警寧澤势腮,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布联贩,位于F島的核電站,受9級特大地震影響捎拯,放射性物質(zhì)發(fā)生泄漏泪幌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一署照、第九天 我趴在偏房一處隱蔽的房頂上張望祸泪。 院中可真熱鬧,春花似錦建芙、人聲如沸没隘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽升略。三九已至,卻和暖如春屡限,著一層夾襖步出監(jiān)牢的瞬間品嚣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工钧大, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留翰撑,地道東北人。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像眶诈,于是被迫代替她去往敵國和親涨醋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內(nèi)容