• Golang基础(8):go interface接口


    一:接口概要


    接口是一种重要的类型,他是一组确定的方法集合。

    一个接口变量可以存储任何实现了接口方法的具体值。
    一个重要的例子就是io.Reader和io.Writer

    type Reader interface {
        Read(p []byte) (n int, err error)
    }
    
    type Writer interface {
        Write(p []byte)(n int, err error)
    }

    如果一个方法实现了io.Reader或者io.Writer里的方法,那么它便实现了io.Reader 或 io.Writer 。 这就

    意味着一个io.Reader 变量可以持有任何一个实现了Read方法的类型的值 [1]

    例 1:
    var r io.Reader
    r = os.Stdin
    os.Stdin 在 os 包中定义:

    var (
        Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
        Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
    )

    NewFile 返回的是一个 *File 结构体,在 os/types.go, File 结构体代码,里面是一个匿名的file结构体

    // File represents an open file descriptor.
    type File struct {
        *file // os specific
    }

    file也是一个结构体,os/file_plan9.go

    type file struct {
        fd int
        name string
        dirinfo *dirInfo // nil unless directory being read
    }

    这个 File 结构体实现了 Read(p []byte) (n int, err error) 方法, 而这个刚好是接口 io.Reader 里的方法
    所以os.Stdin 可以赋值给 io.Reader

     

    例 2:
    var r io.Reader
    r = bufio.NewReader(r)

    在包 bufio.go

    // NewReader returns a new Reader whose buffer has the default size.
    func NewReader(rd io.Reader) *Reader {
        return NewReaderSize(rd, defaultBufSize)
    }
    func NewReaderSize(rd io.Reader, size int) *Reader {
      // Is it already a Reader?
      b, ok := rd.(*Reader)
        if ok && len(b.buf) >= size {
        return b
      }
      if size < minReadBufferSize {
        size = minReadBufferSize
      }
      r := new(Reader)
      r.reset(make([]byte, size), rd)
      return r
    }

     

    2)上面[1]中下划线 接口变量可以持有任何一个实现了Read方法的类型的值,那么接口变量又是什么呢?
    变量里是具体值和这个值类型

    3)一个类型实现了 interface 中的所有方法,我们就说该类型实现了该 interface,
    所以所有的类型都实现了 empty interface,因为任何类型至少有0个方法。
    go中没有显示的关键字来实现interface,只需要实现interface包含的方法即可

    二: 接口定义


    interface类型定义

    type interfaceNamer interface {
        Method1(param_list) return_type
        Method2(param_list) return_type
        ...
    }


    按照约定,只包含一个方法的)接口的名字由方法名加 [e]r 后缀组成,例如 Printer、Reader、Writer、Logger、Converter 等等。还有一些不常用的方式(当后缀 er 不合适时),比如 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像 .NET 或 Java 中那样)

    接口可以屏蔽内部细节,和具体的实现方法。只关注我需要做什么,而不关注怎么做

    三:接口和方法,实现接口的方法


    示例:

    package main
    
    import (
        "fmt"
    )
    
    type Ager interface {
        Get() int
        Set(int)
    }
    
    type Student struct {
        Age int
    }
    
    func (s Student) Get() int {
        return s.Age
    }
    
    func (s *Student) Set(age int) {
        s.Age = age
    }
    
    func funone(ager Ager) {
        ager.Set(10)
        fmt.Println(ager.Get())
    }
    
    func main() {
        //=== 第一部分 stu 可以作为一个变量传递给函数,因为它作为一个struct实现了接口里面的方法
        stu := Student{}
        funone(&stu)
        //=== 第二部分 i 作为一个接口变量,它存储了实现了这个接口方法的类型的值
        var i Ager
        i = &stu
        fmt.Println(i.Get())
    }

     

    在上面程序中,定义来一个名为Ager的 interface 接口,里面定义了2个方法Get() , Set(), 定义了一个 student 的结构体,并且结构体实现了 interface 里面的2个方法Get,Set,我们就说结构体实现了 这个接口
    定义了一个 funcone 的函数,里面参数ager是一个interface类型(interface类型作为函数参数),那么实现了这个接口的任何变量都可以作为参数传递给 funcone 了, (在 main 函数中 第一部分注释)
    interface变量存储了实现者变量的值 (main函数中的 第二部分注释),所以它又可以调用结构体的方法

    一个类型实现了接口里的所有方法,那么这个类型就实现了这个接口

    四:接口嵌套


    一个接口可以包含一个或者多个其他接口

    相当于直接将这些内嵌接口的方法加入到了外层接口中,相当于组合了方法

    还是拿最典型的 Reader 和 Writer 接口

    type Reader interface {
        Read(p []byte) (n int, err error)
    }
    
    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    
    type ReadWriter interface {
        Reader
        Writer
    }

     

    如果一个类型既实现了 Reader 接口,也实现了Writer接口,那么它就自动实现了 ReadWriter 接口
    接口和结构体的定义很相似,也可以完成嵌入接口的功能,嵌入的匿名的接口,可以自动的具备被嵌入的接口的方法

    五: 空interface


     interface{} 空interface,它能存储任意类型的值, 它相当于 c 系的 void * 类型

    定义一个空接口

    var i interface{}
    s := "hello"
    num : 10
    i = s
    i = num

     

    空接口在打印包中的应用,它又是一个可变长参数 fmt/print.go

    func Println(a ...interface{}) (n int, err error) {
        return Fprintln(os.Stdout, a...)
    }

    六:类型断言


    既然接口类型能够实现多种类型的值,那么我们应该有一种方式来检测这种动态类型的值,即运行时变量中存储的值的实际类型。 在执行过程中动态类型可能会有所不同,但是它总是可以分配给接口变量本身的类型。

    那就是 类型断言
    1):type-ok 形式
    1.1:第一种

    直接判断是否是某类型的变量 value,ok = element.(T), 这里value是变量的值,ok是bool类型, element必须是一个 interface 变量, T是断言的类型


    1.2 第二种 简单形式:
    value := element.(T)

     示例:

    package main
    
    import (
        "fmt"
        "math"
    )
    
    type Square struct {
        side float32
    }
    
    type Circle struct {
        radius float32
    }
    
    type Shaper interface {
        Area() float32
    }
    
    func main() {
        var area Shaper
        sq1 := new(Square)
        sq1.side = 5
    
        area = sq1
        //is Square the type of area
        if t, ok := area.(*Square); ok {
            fmt.Printf("The type of area is: %T 
    ", t)
        }
    
        if u, ok := area.(*Circle); ok {
            fmt.Printf("The type of area is: %T 
    ", u)
        } else {
            fmt.Println("area does not cotain a variable of type Cicle")
        }
        //输出结果
        // go run assert1.go
        // The type of area is: *main.Square
        // area does not cotain a variable of type Cicle
    
    }
    
    func (sq *Square) Area() float32 {
        return sq.side * sq.side
    }
    
    func (ci *Circle) Area() float32 {
        return ci.radius * ci.radius * math.Pi
    }

     

    2):switch 形式
    上面也可以用 switch的形式

    switch t := area.(type) {
    case *Square:
       fmt.Printf("Type Square %T with value %v
    ", t, t)
    case *Circle:
           fmt.Printf("Type Circle %T with value %v
    ", t, t)
        case nil:
            fmt.Printf("nil value: nothing to check?
    ")
        default:
        fmt.Printf("Unexpected type %T
    ", t)
    }

     

    参考:
    1. http://wiki.jikexueyuan.com/project/the-way-to-go/11.4.html
    2. https://research.swtch.com/interfaces
    3. https://blog.golang.org/laws-of-reflection

  • 相关阅读:
    菜根谭#219
    菜根谭#218
    菜根谭#217
    菜根谭#216
    有了OpenMP,MPI,为什么还要MapReduce? (转载)
    人生就是一场战略
    SDAccel 开发环境
    各种加速卡 异构计算
    Nvidia Tesla (GPGPU)系列发展流程
    Nvidia 架构发展
  • 原文地址:https://www.cnblogs.com/jiujuan/p/10230200.html
Copyright © 2020-2023  润新知