一看就懂系列之Golang的接口

https://blog.csdn.net/u011957758/article/details/81150622

前言

接口在面向對象編程中是經常使用的招式,也是體現多態很重要的手段。
是的。Golang中也有接口這玩意兒。

本文將以通俗易懂的方式,說清楚Golang的接口。

10s後,以下知識點即將到達戰場:
1.爲什麼需要接口?
2.接口是什麼?如何定義?
3.接口實戰初體驗
4.如何測試是否已實現該接口?
5.空接口&類型斷言
6.接口零值
7.一個類型實現多個接口
8.指針與值類型實現接口的區別
9.接口嵌套

正文

1.爲什麼需要接口?

多數情況下,數據可能包含不同的類型,卻會有一個或者多個共同點,這些共同點就是抽象的基礎。前文講到的Golang繼承解決的是is-a的問題,單一繼承的關係。但是當不同的父類具有相同的行爲的時候,單一繼承就沒法解決了。

於是乎,接口出現了。接口可以理解爲某一個方面的抽象,可以是多對一的(多個類型實現一個接口),這也是多態的體現。解決了上文一對一的問題。

2.接口是什麼?如何定義?

是什麼
接口是一組僅包含方法名、參數、返回值的未具體實現的方法的集合

如果實現了接口的所有方法,則認爲實現了該接口,無需在該類型上顯示的添加聲明

這個解釋下,加深印象,在php中接口是長這樣的:

//定義接口
interface base{
   public function getName();
}

//學生類
class student implements base{
   public function getName(){
      echo "咖啡色的羊駝";
   }
}

這裏有個關鍵字:implements

這樣的聲明稱之爲顯示的,而在Golang中接口是隱式地實現。(埋個伏筆看下文)

定義

type interfaceName interface { 
    // 方法列表 
    GetName() string
} 

3.接口實戰初體驗

實際編程中呢,接口的命名大夥兒喜歡使用er結尾。當然這個看個人喜好。

上代碼:

    package main

    import (
        "fmt"
    )

    // 定義一個接口
    type People interface {
        ReturnName() string
    }

    // 定義一個結構體
    type Student struct {
        Name string
    }

    // 定義結構體的一個方法。
    // 突然發現這個方法同接口People的所有方法(就一個),此時可直接認爲結構體Student實現了接口People
    func (s Student) ReturnName() string {
        return s.Name
    }

    func main() {
        cbs := Student{Name:"咖啡色的羊駝"}

        var a People
        // 因爲Students實現了接口所以直接賦值沒問題
        // 如果沒實現會報錯:cannot use cbs (type Student) as type People in assignment:Student does not implement People (missing ReturnName method)
        a = cbs       
        name := a.ReturnName() 
        fmt.Println(name) // 輸出"咖啡色的羊駝"
    }

4.如何測試是否已實現該接口?

使用接口特有的斷言判斷來實現(下文還會再次提到,加深印象)。

語法:x.(T)
這樣的語法只適應於x是interface類型

接着上文例子,繼續上代碼:

    // 由於x.(T)只能是接口類型判斷,所以傳參時候,傳入的是接口類型
    // 爲何test的類型可以是一個空接口?埋伏筆下文便知。
    func CheckPeople(test interface{}) {
        if _, ok := test.(People); ok {
            fmt.Printf("Student implements People")
        }
    }


    func main() {
        cbs := Student{Name:"咖啡色的羊駝"}
        CheckPeople(cbs) // Student implements People
    }

5.空接口&類型斷言

空接口

空接口就是不包含任何方法的接口。正因爲如此,所有的類型都實現了空接口

雖然空接口起不到任何作用,但是空接口在需要存儲任何類型數值的時候非常有用,這也回答了上文的問題,因爲空接口可以存儲任意類型的數據。

    // 定義cbs爲空接口
    var cbs interface{}
    var i int = 5
    var s string = "Hello world"
    // cbs可以存儲任意類型的數值
    cbs = i
    cbs = s

類型斷言

既然空接口可以存儲任意類型,那麼如何區分不同的類型?
常用的有兩種方法:Comma-ok斷言、switch判斷

上代碼:

    package main

    import (
        "fmt"
    )

    // 定義一個結構體
    type Student struct {
        Name string
    }

    // 類型斷言
    func main() {
        Params := make([]interface{}, 3)
        Params[0] = 88                   // 整型
        Params[1] = "咖啡色的羊駝"         // 字符串
        Params[2] = Student{Name: "cbs"} // 自定義結構體類型

        // Comma-ok斷言
        for index, v := range Params {
            if _, ok := v.(int); ok {
                fmt.Printf("Params[%d] 是int類型 \n", index)
            } else if _, ok := v.(string); ok {
                fmt.Printf("Params[%d] 是字符串類型\n", index)
            } else if _, ok := v.(Student); ok {
                fmt.Printf("Params[%d] 是自定義結構體類型\n", index)
            } else {
                fmt.Printf("list[%d] 未知類型\n", index)
            }
        }

        // switch判斷
        for index, v := range Params {
            switch  value := v.(type) {
            case int:
                fmt.Printf("Params[%d] 是int類型, 值:%d \n", index,value)
            case string:
                fmt.Printf("Params[%d] 是字符串類型, 值:%s\n", index,value)
            case Student:
                fmt.Printf("Params[%d] 是Person類型, 值:%s\n", index,value)
            default:
                fmt.Printf("list[%d] 未知類型\n", index)
            } 

        }  
    }

6.接口零值

接口的零值是nil

    package main

    import (
        "fmt"
    )

    type People interface {  
        GetName() string
    }

    // 輸出 "cbs is nil 類型"
    func main() {  
        var cbs People
        if cbs == nil {
            fmt.Println("cbs is nil 類型")  
        }
    }

7.一個類型實現多個接口

    package main

    import (
        "fmt"
    )

    type People interface {
        ReturnName() string
    }

    type Role interface {
        ReturnRole() string
    }

    type Student struct {
        Name string
    }

    func (s Student) ReturnName() string {
        return s.Name
    }

    func (s Student) ReturnRole() string {
        return "學生"
    }

    func main() {
        cbs := Student{Name: "咖啡色的羊駝"}

        var a People  // 定義a爲People接口類型
        var b Role    // 定義b爲Role接口類型

        a = cbs // 由於Student實現了People所有方法,所以接口實現成功,可直接賦值
        b = cbs // 由於Student實現了Role所有方法,所以接口實現成功,可直接賦值

        name := a.ReturnName()
        fmt.Println(name) // 輸出"咖啡色的羊駝"

        role := b.ReturnRole()
        fmt.Println(role) // 輸出"學生"
    }

也說明一個東西:實現了某個接口的類型,還可以有其它的方法。只要是方法實現包含接口的即可。

8.指針與值類型實現接口的區別

    package main

    import (
        "fmt"
    )

    type People interface {
        ReturnName() string
    }

    type Student struct {
        Name string
    }

    type Teacher struct {
        Name string
    }

    func (s Student) ReturnName() string {
        return s.Name
    }

    func (t *Teacher) ReturnName() string {
        return t.Name
    }

    func main() {
        cbs := Student{Name: "咖啡色的羊駝"}
        sss := Teacher{Name: "咖啡色的羊駝的老師"}

        // 值類型
        var a People
        a = cbs 
        name := a.ReturnName()
        fmt.Println(name)

        // 指針類型
        // a = sss <- 這樣寫不行!!!
        a = &sss // 由於是指針類型,所以賦值的時候需要加上&
        name = a.ReturnName()
        fmt.Println(name) // 輸出"咖啡色的羊駝的老師"
    }

“a = sss”這樣寫會發生報錯:

    cannot use sss (type Teacher) as type People in assignment:
    Teacher does not implement People (ReturnName method has pointer receiver)

因爲是Teacher的指針實現了ReturnName方法,Teacher本身沒實現。

9.接口嵌套

類似於PHP的接口繼承,Golang也有它的接口嵌套。

    package main

    import (
        "fmt"
    )

    type People interface {
        ReturnName() string
    }

    type Role interface {
        People // 接口嵌套
        ReturnRole() string
    }

    type Student struct {
        Name string
    }

    func (s Student) ReturnName() string {
        return s.Name
    }

    func (s Student) ReturnRole() string {
        return "學生"
    }



    func main() {
        cbs := Student{Name: "咖啡色的羊駝"}

        var a Role
        a = cbs 
        name := a.ReturnName()
        fmt.Println(name)

        role := a.ReturnRole()
        fmt.Println(role) 
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章