zoukankan      html  css  js  c++  java
  • Go语言基础之接口定义

    Go语言基础之接口定义

    接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

    一、接口类型

    在Go语言中接口(interface)是一种类型,一种抽象的类型,引用类型。

    interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。

    为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型。

    二、 为什么要使用接口

    type Cat struct{}
    
    func (c Cat) Say() string { return "喵喵喵" }
    
    type Dog struct{}
    
    func (d Dog) Say() string { return "汪汪汪" }
    
    func main() {
        c := Cat{}
        fmt.Println("猫:", c.Say())
        d := Dog{}
        fmt.Println("狗:", d.Say())
    }
    

    上面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码,如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去。那我们能不能把它们当成“能叫的动物”来处理呢?

    像类似的例子在我们编程过程中会经常遇到:

    比如一个网上商城可能使用支付宝、微信、银联等方式去在线支付,我们能不能把它们当成“支付方式”来处理呢?

    比如三角形,四边形,圆形都能计算周长和面积,我们能不能把它们当成“图形”来处理呢?

    比如销售、行政、程序员都能计算月薪,我们能不能把他们当成“员工”来处理呢?

    Go语言中为了解决类似上面的问题,就设计了接口这个概念。接口区别于我们之前所有的具体类型,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么。

    三、接口的定义

    Go语言提倡面向接口编程。

    每个接口由数个方法组成,接口的定义格式如下:

    type 接口类型名 interface{
        方法名1( 参数列表1 ) 返回值列表1
        方法名2( 参数列表2 ) 返回值列表2
        …
    }
    

    其中:

    • 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义
    • 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
    • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

    举个例子:

    type writer interface{
        Write([]byte) error
    }
    

    当你看到这个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的Write方法来做一些事情。

    四、实现接口的条件

    一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表

    我们来定义一个Sayer接口:

    // Sayer 接口
    type Sayer interface {
        say()
    }
    

    定义dogcat两个结构体:

    type dog struct {}
    
    type cat struct {}
    

    因为Sayer接口里只有一个say方法,所以我们只需要给dogcat分别实现say方法就可以实现Sayer接口了。

    // dog实现了Sayer接口
    func (d dog) say() {
        fmt.Println("汪汪汪")
    }
    
    // cat实现了Sayer接口
    func (c cat) say() {
        fmt.Println("喵喵喵")
    }
    

    接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。

    五、接口类型变量

    那实现了接口有什么用呢?

    接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Sayer类型的变量能够存储dogcat类型的变量。

    func main() {
        var x Sayer // 声明一个Sayer类型的变量x
        a := cat{}  // 实例化一个cat
        b := dog{}  // 实例化一个dog
        x = a       // 可以把cat实例直接赋值给x
        x.say()     // 喵喵喵
        x = b       // 可以把dog实例直接赋值给x
        x.say()     // 汪汪汪
    }
    

    如果我们仅仅只是想验证一下,某个结构体是否实现了接口,可以这么做

    // 1、如果下述代码可以赋值成功,则证明结构体实现了接口,因为此处我们只想验证,所以变量名无关紧要,推荐用_代表
    var _ 接口类型 = &结构体{}  
    
    // 2、例如:
    var _ DuckInterface = &PDuck{}
    

    image-20211101221246246

    // 摘自gin框架routergroup.go 体味此处_的妙用
    type IRouter interface{ ... }
    
    type RouterGroup struct { ... }
    
    var _ IRouter = &RouterGroup{}  // 确保RouterGroup实现了接口IRouter
    

    任意的类型都可以赋值给空接口类型

    type Empty interface {
    }
    //3 空接口(没有方法,所有类型其实都实现了空接口,于是:任意的类型都可以赋值给空接口类型)
    var a Empty=10
    a="ssddd"
    a = TDuck{}
    fmt.Println(a)
    

    六、接口零值

    接口的零值是 nil。对于值为 nil 的接口,其底层值(Underlying Value)和具体类型(Concrete Type)都为 nil

    package main
    
    import "fmt"
    
    type Describer interface {  
        Describe()
    }
    
    func main() {  
        var d1 Describer
        if d1 == nil {
            fmt.Printf("d1 is nil and has type %T value %v\n", d1, d1)
        }
    }
    

    上面程序里的 d1 等于 nil,程序会输出:

    d1 is nil and has type <nil> value <nil>
    

    对于值为 nil 的接口,由于没有底层值和具体类型,当我们试图调用它的方法时,程序会产生 panic 异常。

    package main
    
    type Describer interface {
        Describe()
    }
    
    func main() {  
        var d1 Describer
        d1.Describe()
    }
    

    在上述程序中,d1 等于 nil,程序产生运行时错误 panicpanic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0xc8527]

    七、总结

    1. 接口定义

      • 接口名单词后面加er,表示是接口
      • 接口名和方法名大写表示可以被包之外代码可以访问
      type 接口类型名 interface{
          方法名1( 参数列表1 ) 返回值列表1
          方法名2( 参数列表2 ) 返回值列表2
          …
      }
      
    2. 如果一个结构体实现接口中所有的方法,那么就实现了这个接口

    3. 接口是一种类型,引用类型,默认值:nil

    4. 空接口可以接收任何类型

    5. 判断结构体是否实现了接口:var _ 接口类型 = &结构体{}

  • 相关阅读:
    poj 3666 Making the Grade
    poj 3186 Treats for the Cows (区间dp)
    hdu 1074 Doing Homework(状压)
    CodeForces 489C Given Length and Sum of Digits...
    CodeForces 163A Substring and Subsequence
    CodeForces 366C Dima and Salad
    CodeForces 180C Letter
    CodeForces
    hdu 2859 Phalanx
    socket接收大数据流
  • 原文地址:https://www.cnblogs.com/randysun/p/15496477.html
Copyright © 2011-2022 走看看