首頁 > 軟體

從零開始學Golang的介面

2022-03-24 13:00:32

前言

介面在物件導向程式設計中是經常使用的招式,也是體現多型很重要的手段。
是的。Golang中也有介面這玩意兒。

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] 是字串型別, 值:%sn", index,value)
            case Student:
                fmt.Printf("Params[%d] 是Person型別, 值:%sn", 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) 
    }

到此這篇關於從零開始學Golang的介面的文章就介紹到這了,更多相關Golang 介面內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com