Go基础之--接口

时间:2022-05-06
本文章向大家介绍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("支付失败,失败原因:%vn",err)
        fmt.Println("使用支付宝支付")
        err = phone.PayMoney("aLiPay",100)
        if err != nil{
            fmt.Printf("支付失败,失败原因:%vn",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 intn",index)
        case float32:
            fmt.Printf("第%d个参数is float32n",index)
        case string:
            fmt.Printf("第%d个参数is stringn",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 Dogn")
    case *Cat:
        t.Eat()
        fmt.Printf("t is Catn")
    }

}

判断一个变量是否实现了指定的接口

在最开始的时候写了一个关于理解接口的例子,如果我们现在想要判断一个变量是否实现了指定的支付的接口,可以通过如下代码实现

weChat := &WeChatPay{}
// 这里需要一个空接口
var tmp interface{} = weChat
_,ok := tmp.(Pay)
if ok{
    fmt.Println("weChat is implement Pay interface")
}