日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 編程語言 正文

從零開始學Golang的接口_Golang

作者:咖啡色的羊駝 ? 更新時間: 2022-05-27 編程語言

前言

接口在面向?qū)ο缶幊讨惺墙?jīng)常使用的招式,也是體現(xiàn)多態(tài)很重要的手段。
是的。Golang中也有接口這玩意兒。

1.為什么需要接口?

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

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

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

是什么
接口是一組僅包含方法名、參數(shù)、返回值的未具體實現(xiàn)的方法的集合。

如果實現(xiàn)了接口的所有方法,則認為實現(xiàn)了該接口,無需在該類型上顯示的添加聲明。

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

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

這里有個關鍵字:implements。

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

定義

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

3.接口實戰(zhàn)初體驗

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

上代碼:

?? ?package main

?? ?import (
?? ??? ?"fmt"
?? ?)
?? ?
?? ?// 定義一個接口
?? ?type People interface {
?? ??? ?ReturnName() string
?? ?}
?? ?
?? ?// 定義一個結(jié)構(gòu)體
?? ?type Student struct {
?? ??? ?Name string
?? ?}
?? ?
?? ?// 定義結(jié)構(gòu)體的一個方法。
?? ?// 突然發(fā)現(xiàn)這個方法同接口People的所有方法(就一個),此時可直接認為結(jié)構(gòu)體Student實現(xiàn)了接口People
?? ?func (s Student) ReturnName() string {
?? ??? ?return s.Name
?? ?}
?? ?
?? ?func main() {
?? ??? ?cbs := Student{Name:"咖啡色的羊駝"}
?? ?
?? ??? ?var a People
?? ??? ?// 因為Students實現(xiàn)了接口所以直接賦值沒問題
?? ??? ?// 如果沒實現(xiàn)會報錯: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.如何測試是否已實現(xiàn)該接口?

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

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

接著上文例子,繼續(xù)上代碼:

?? ?// 由于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.空接口&類型斷言

空接口

空接口就是不包含任何方法的接口。正因為如此,所有的類型都實現(xiàn)了空接口。

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

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

類型斷言

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

上代碼:

?? ?package main
?? ?
?? ?import (
?? ??? ?"fmt"
?? ?)
?? ?
?? ?// 定義一個結(jié)構(gòu)體
?? ?type Student struct {
?? ??? ?Name string
?? ?}
?? ?
?? ?// 類型斷言
?? ?func main() {
?? ? ? ?Params := make([]interface{}, 3)
?? ??? ?Params[0] = 88 ? ? ? ? ? ? ? ? ? // 整型
?? ??? ?Params[1] = "咖啡色的羊駝" ? ? ? ? // 字符串
?? ??? ?Params[2] = Student{Name: "cbs"} // 自定義結(jié)構(gòu)體類型
?? ??? ?
?? ??? ?// 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] 是自定義結(jié)構(gòu)體類型\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.一個類型實現(xiàn)多個接口

    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實現(xiàn)了People所有方法,所以接口實現(xiàn)成功,可直接賦值
        b = cbs // 由于Student實現(xiàn)了Role所有方法,所以接口實現(xiàn)成功,可直接賦值
        
        name := a.ReturnName()
        fmt.Println(name) // 輸出"咖啡色的羊駝"
    
        role := b.ReturnRole()
        fmt.Println(role) // 輸出"學生"
    }

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

8.指針與值類型實現(xiàn)接口的區(qū)別

    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"這樣寫會發(fā)生報錯:

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

因為是Teacher的指針實現(xiàn)了ReturnName方法,Teacher本身沒實現(xiàn)。

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) 
    }

原文鏈接:https://blog.csdn.net/u011957758/article/details/81150622

欄目分類
最近更新