1. 程式人生 > >Go基礎之--介面

Go基礎之--介面

定義

在Go語言中,一個類只要實現了介面要求的所有函式,我們就說這個類實現了該介面

interface型別可以定義一組方法,用來表示一個物件的行為特徵,interface不能包含任何變數,介面是引用型別。

舉個簡單的例子,一個動物的介面,動物有吃的能力,有叫的能力,等等,這裡省略,假如動物就只有吃和叫的能力。

package main

import "fmt"

type Animal interface {
    Eat()
    Talk()
}

type Dog struct{

}

func (d *Dog) Eat(){
    fmt.Println("dog eating.....")
}

func (d *Dog) Talk(){
    fmt.Println("dog talking....")
}

type Cat struct{

}

func (d *Cat) Eat(){
    fmt.Println("cat eating.....")
}

func (d *Cat) Talk(){
    fmt.Println("cat talking....")
}



func main(){
    var d Dog

    var a Animal
    a = &d
    a.Eat()
    a.Talk()

    var c Cat
    a = &c
    a.Eat()
    a.Talk()
}

上面這個例子中,Cat和Dog實現了Animal的所有方法,所以Cat和Dog都是動物

小結一下:
Go中的介面不需要顯示的實現,只要一個物件實現了介面型別中的所有方法,那麼這個物件就實現了這個介面,當然如果一個物件實現了多個interface型別的方法,那麼這個物件就實現了多個介面

用於理解介面的一個例子

我們都知道現在的手機有很多支付方式,如:微信支付,支付寶支付,銀聯支付等等,這裡可以通過一個實現支付介面的例子來理解介面

// 定義一個支付的介面
type Pay interface {
    pay(userId int64,money float32) error
}

// 這裡定義一個struct
type AliPay struct {

}
// 這裡給AliPay新增一個支付方法,實現了Pay介面中的pay方法
func (a *AliPay) pay(userId int64,money float32) error{
    fmt.Println("1.連線到阿里支付的伺服器")
    fmt.Println("2.連線到對應的使用者")
    fmt.Println("3.檢查餘額")
    fmt.Println("4.扣錢")
    fmt.Println("5.返回支付是否成功")

    return nil
}

// 微信支付
type WeChatPay struct {

}

// 這裡也是實現了Pay介面中的pay方法
func (w *WeChatPay) pay(userId int64,money float32) error{
    fmt.Println("1.連線到微信支付的伺服器")
    fmt.Println("2.連線到對應的使用者")
    fmt.Println("3.檢查餘額")
    fmt.Println("4.扣錢")
    fmt.Println("5.返回支付是否成功")

    return nil
}

// 這裡定義一個手機struct,並通過字典方式儲存自己開通的支付方式
type Phone struct {
   PayMap map[string]Pay
}

func (p *Phone) OpenWeChatPay(){
    weChatPay := &WeChatPay{}
    p.PayMap["weChatPay"] = weChatPay
}

func (p *Phone) OpenAliPay(){
    AliPay := &AliPay{}
    p.PayMap["aLiPay"] = AliPay
}

func (p *Phone) PayMoney(name string,money float32)(err error){
    pay,ok:= p.PayMap[name]
    if !ok{
        err = fmt.Errorf("不支援【%s】支付方式",name)
        return
    }
    err = pay.pay(1024,money)
    return
}


func main(){
    // 這裡切記 字典型別的資料是需要初始化的
    phone := &Phone{
        PayMap:make(map[string]Pay,10),
}

    // 這裡是用於開通自己有哪些支付方式
    //phone.OpenWeChatPay()
    phone.OpenAliPay()


    err := phone.PayMoney("weChatPay",100)
    if err != nil{
        // 如果微信支付失敗了,用支付寶支付
        fmt.Printf("支付失敗,失敗原因:%v\n",err)
        fmt.Println("使用支付寶支付")
        err = phone.PayMoney("aLiPay",100)
        if err != nil{
            fmt.Printf("支付失敗,失敗原因:%v\n",err)
            return
        }
    }
    fmt.Println("支付成功,歡迎再次光臨")
}

當然可以把上面中關於開通支付方式的兩個方法,用一個通用的方法實現,如:

func (p *Phone) OpenPay(name string,pay Pay){
    // 可以把上面兩個方法更改為這一個方法
    p.PayMap[name] = pay
}

空介面

空介面沒有任何方法,所有的型別都實現了空介面,空介面什麼型別都可以存,如下例子:

package main

import "fmt"

func main()  {
    // 通過這個例子我們可以發現我們定義的一個空介面可以存任何型別的變數
    var a interface{}
    var b int = 10
    a = b
    fmt.Println(a)

    var c string = "hello"
    a = c
    fmt.Println(a)
}

介面的巢狀

一個介面可以巢狀在另外的接口裡面,同時一個介面也可以巢狀多個介面
通過下面的例子來理解介面巢狀的概念

package main

import "fmt"

// 這裡定義一個Eater介面
type Eater interface {
    Eat()
}

// 這裡定義一個Talker介面
type Talker interface {
    Talk()
}

// 這裡定義個動物的介面,同時嵌套了Eater和Talker介面
type Animal interface {
    Eater
    Talker
}

// 這裡定義一個Dog的struct,並實現talk方法和eat方法,這樣就實現了動物的介面
type Dog struct {

}

func (d *Dog) Talk(){
    fmt.Println("talk....")
}

func (d *Dog) Eat(){
    fmt.Println("eating....")
}

func main() {
    d := &Dog{}
    var a Animal
    a = d
    a.Eat()
    a.Talk()
}

型別斷言

如果我們反向想要知道這個介面變數裡面實際儲存的是哪個型別的物件,可以用下面方法:
通過下面這個例子理解:

package main

import (
    "fmt"
)

type Animal interface {
    Eat()
    Talk()
}

type Dog struct{

}

func (d *Dog) Eat(){
    fmt.Println("dog eating.....")
}

func (d *Dog) Talk(){
    fmt.Println("dog talking....")
}

type Cat struct{

}

func (c *Cat) Eat(){
    fmt.Println("cat eating.....")
}

func (c *Cat) Talk(){
    fmt.Println("cat talking....")
}

func justify(a Animal){
    // 進行強制轉換,如果轉換失敗則提示錯誤
    dog,ok := a.(*Dog)
    if !ok{
        fmt.Println("convert to dog failed")
        return
    }
    dog.Eat()
}

func main()  {
    // 分別例項化一個Dog和Cat,並通過justify來進行判斷
    d := &Dog{}
    var a Animal
    a = d
    a.Eat()
    justify(a)

    c := &Cat{}
    a = c
    justify(a)
}

再寫一個例子,用於判斷函式中傳入的引數的型別:

package main

import (
    "fmt"
)
// 這裡通過...interface{}表示傳入的是可變引數
func justify(items ...interface{}){
    for index,v := range items{
       //v.(type)表示獲取變數的型別
        switch v.(type){
        case int:
            fmt.Printf("第%d個引數is int\n",index)
        case float32:
            fmt.Printf("第%d個引數is float32\n",index)
        case string:
            fmt.Printf("第%d個引數is string\n",index)
        }
    }
}

func main() {
    var a float32
    var b string
    var c int
    justify(a,b,c)
}

關於貓和狗程式碼例子中justify方法還可以更改為:

func justify2(a Animal){
    switch t:=a.(type){
    case *Dog:
        t.Eat()
        fmt.Printf("t is Dog\n")
    case *Cat:
        t.Eat()
        fmt.Printf("t is Cat\n")
    }
}

判斷一個變數是否實現了指定的介面

在最開始的時候寫了一個關於理解介面的例子,如果我們現在想要判斷一個變數是否實現了指定的支付的介面,可以通過如下程式碼實現

weChat := &WeChatPay{}
// 這裡需要一個空介面
var tmp interface{} = weChat
_,ok := tmp.(Pay)
if ok{
    fmt.Println("weChat is implement Pay interface")
}