• go语言之接口


    一:接口的基本概念

    1 接口声明

    接口字面量,接口命名类型,接口声明使用interface关键字。

      1)接口字面量类型声明语法如下:

    interface{
          methodSignature1    
          methodSignature1    
    }

        2)接口命名类型使用type关键字声明

    type   interfaceName interface {
           MethodSignature1
           MethodSignature2
    }
    接口定义大括号内可以是方法声明的集合,也可以嵌入另一个接口类型匿名字段,还可以是二者的混合。
    接口支持嵌入匿名接口宇段,就是一个接口定义里面可以包括其他接口, Go编译器会自动进行展开 理,
    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 
    }
    type ReadWr ter interface { 
        Reader 
        Wr te(p []byte) (n int, err error) 
    }
    type ReadWriter interface { 
        Read(p []byte) (n int, err error) 
        Write(p []byte) (n nt err error)
    }

      3)方法声明

    MethodName (InputTypeList)OutputTypeList 

         4)声明新接口类型的特点

    (I)接口的命名一般以“er ”结尾
    (2)接口定义的内部方法声明不需要 func 引导。
    (3)在接口定义中,只有方法声明没有方法实现。

      5) 接口的定义与实现

    package main
    
    import (
        "fmt"
    )
    
    type Humaner interface {
        //方法
        Say()
    }
    //学生结构体
    type Student struct {
        name string
        score int
    }
    
    func (s*Student) Say()  {
        fmt.Println("Student[%s,%d]瞌睡不断
    ",s.name,s.score)  
    }
    type Teacher struct {
        name string
        group string
    }
    
    func (t *Teacher) Say()  {
        fmt.Println("Teacher[%s,%s] 诲人不倦
    ",t.name,t.group)
    }
    //自定义类型
    type  Mystr string
    
    func (str Mystr) Say() {
        fmt.Println("Mystr[%s] 统治醒醒,还有个bug
    ",str)
    }
    //参数为接口类型
    func Whosay(i Humaner)  {
        i.Say()
    }
    func main(){
        s :=&Student{"学生",88}
        t :=&Teacher{"老师","GO语言"}
        var tmp Mystr="字符串"
        s.Say()
        t.Say()
        tmp.Say()
        //多态,条用同一接口不同的表现
        Whosay(s)
        Whosay(t)
        Whosay(tmp)
    
        //make()
        x :=make([]Humaner,3)
        x[0],x[1],x[2] = s,t,tmp
        for _,value :=range x{
            value.Say()
        }
    }

      接口的继承

    package main
    
    import "fmt"
    //定义接口
    type Humaner interface {
        //方法
        Say()
    }
    type Personer interface {
        //相当于写了say() 方法的继承
        Humaner
        //唱歌
        Sing(lyrics string)
    }
    type Student struct {
        name string
        score int
    }
    func (s *Student) Say() {
        fmt.Printf("Student[%s,%d] 瞌睡不断
    ",s.name,s.score)   //Student[学生,80] 瞌睡不断
    }
    
    func (s *Student) Sing(lyrics string){
        fmt.Printf("Student sing[%s]!!
    ",lyrics)                  //Student sing[葫芦娃]!!
    }
    func main()  {
        s := &Student{"学生",80}
        //调Personer方法
        var p  Personer
        p = s
        p.Say()
        p.Sing("葫芦娃")
    }

    2 接口初始化

    单纯地声明一个接口变量没有任何意义,接口只有被初始化为具体的类型时才有意义。接口作为
    一个胶水层或抽象层,起到抽象和适配的作用 。没有初始化的接口变量,其默认值是 nil。

    3 接口绑定具体类型的实例的过程称为接口初始化。接口变量支持两种直接初始化方法

      1)实例赋值接口

    如果具体类型实例的方法集是某个接口的方法集的超集,则称该具体类型实现了接口,可
    以将该具体类型的实例直接赋值给接口类型的变 ,此时编译器会进行静态的类型检查。接口
    被初始化后,调用接口的方法就相当于调用接口绑定的具体类型的方法,这就是接口调用的语义。

      2)接口变量赋值接口变量

    已经初始化的接口类型变量a直接赋值给另一种接口变量b ,要求b的方法集是a的方法即
    的子集 此时 Go 编译器会在编译时进行方法集静态检查 这个过程也是接口初始化的一种
    方式,此时接口变量 绑定的具体实例是接口变量 绑定的具体实例的副本。
    file ,_ := os .OpenFile (” notes.txt”, os.O_RDWR |os.O CREATE , 0755 ) 
    var rw io .ReadWriter = file 
    //io.ReadWriter 口可以直接赋位给 io.Writer接口变量
    var  w  o.Writer = rw 

    4 接口方法的调用

    接口方法调用和普通的函数调用是有区别的。接口方法调用的最终地址是在运行期决定的,
    将具体类型变量赋值给接口后,会使用具体类型的方法指针初始化接口变量,当调用接口变量的方法时,
    实际上是间接地调用实例的方法。接口方法调用不是 种直接的调用,有 定的运行时开销

      直接调用禾初始化的接口变 的方法会产生 panic 。

    package main
    type printer interface {
        Print()
    }
    type S struct {}
    func (s S) Print()  {
        println("print")
    }
    func main()  {
        var i  printer
        //没有初始化的接口调用其他方法会产生panic
        //必须初始化
        i = S{}
        i.Print()
    }

    5 接口动态类型和静态类型

      1)动态类型

    接口绑定的具体实例的类型称为接口的动态类型。接口可以绑定不同类型的实例,所以接
    口的动态类型是随着其绑定的不同类型实例而发生变化的。

      2) 静态类型

    接口被定义时, 其类型就已经被确定 这个类型 接口的静态类型。接口的静态类型在其
    定义 就被确定,静态类型的本质特征就是接口的方法签名集合。两个接口如果方法签名集合
    相同(方法的顺序可以不同),则这两个接口在语义上完全等价,它们之间不需要强制类型转换就可以相互赋值。
    原因是 Go 编译器校验接口是否能赋值,是比较二者的方法集,而不是看具体接口类型名。

    二: 接口运算

    1 语法:

    i.(TypeNname) 
     i必须是接口变 ,如果是具体类型变量,则编译器会报 on interface type xxx on left, 
    TypeNname 可以是接口类型名,也可以是具体类型名。

    2 接口查询的两层含义

    (1)如果 TypeNname 是一个具体类型名,则类型断言用于判断接口变量 绑定的实例类
    型是否就是具体类型 TypeNname
    (2)如果 TypeName 是一个接口类型名,则类型断言用于判断接口变量 绑定的实例类型
    是否同时实现了 TypeName 接口。

    3 接口断言的两种语法表现

      直接赋值模式

    o := i.(TypeName)

      语义分析:

    (1) TypeNam 是具体类型名,此时如果接 绑定的实例类型就是具体类型 TypeName,
    变量 。的类型就是 TypeName 变量。的值就是接口绑定的实例值的副本(当然实例可能是
    指针值,那就是指针值的副本)
    (2) TypeName 是接口类型名 如果接口i绑定的实例类型满足接口类型 TypeName ,则变量o
    的类型就是接口类型 TypeName,o底层绑定的具体类型实例是i绑定的实例的副本(当然实例可能是指针值,那就是指针值的副本〉。
    (3)如果上述两种情况都不满足, 则程序抛 panic

      示例

    package main
    
    import "fmt"
    
    type Inter interface {
        Ping()
        Pang()
    }
    type Anter interface {
        Inter
        String()
    }
    type St struct {
        Name string
    }
    
    func (St) Ping() {
        println("ping")
    }
    func (*St) Pang()  {
        println("pang")
    }
    func main()  {
        st := &St{"andes"}
        var i  interface{}=st
        //判断i绑定的实例是否实现了接口类型Inter
        o  :=i.(Inter)
        o.Ping()
        o.Pang()
        //如下语句会引发panic,因为i没有实现接口Anter
        //p :=i.(Anter)
        //p.String()
        //判断 i绑定的实例是否就是具体类型St
        s := i.(*St)
        fmt.Printf("%s",s.Name)
    }

    4  comma,ok 表达模式如下

    if o,ok :=i.(TypeName);ok{
    }

      语法分析

    (1)TypeName是具体类型名,此时如果接口i绑定的实例类型就是具体类型TypeName,则ok为true变量。变量o的类型就是TypeName,
    变量o的值就是接口绑定的实例值的副本(当然实例可能是指针值,那就是指针值的副本)
    (2)TypeName是接口类型名,此时如果接口i绑定的实例类型满足接口类型TypeName,则ok为true,变量o的类型就是接口类型 
    TypeName,o底层绑定的具体类型实例是i绑定的实例的副本(当然实例可能是指针值,那就是指针值的副本)。 (
    3)如果上述两个都不满足,则 ok为 false 变量o是TypeName 类型的“零值”,此种条 件分支下程序逻辑不应该再去引用o,因为此时的o没有意义

      示例:

    package main
    
    import (
        "fmt"
    )
    
    type Inter interface {
        Ping()
        Pang()
    }
    type Anter interface {
        Inter
        String()
    }
    type St struct {
        Name string
    }
    func (St) Ping(){
        println("ping")
    }
    func (*St) Pang(){
        println("pang")
    }
    func main(){
        st :=  &St{"andes"}
        var i  interface{} = st
            //判断i绑定的实例是否实现了接口类型Inter
        if o,ok  := i.(Inter);ok{
            o.Ping()  //ping
            o.Pang()  //pang
        }
        if p,ok  := i.(Anter);ok{
            //i没有实现接口Anter,所以程序不会执行到这里
            p.String()
        }
        //判断i 绑定的实例是否就是具体类型St
        if s,ok  := i.(*St);ok{
            fmt.Printf("%s",s.Name)  //andes
        }
    }

    5  类型查询

      语法格式:

    switch v :=工. (type) { 
    case typel : 
    xx xx 
    case type2 : 
    xx xx 
    default : 
    xx xx 

      语义分析:

    语义:
    1 查询一个接口变量底层绑定的底层变量的具体类型是什么,
    2 查询接口变量绑定的底层变量是否实现了其他接口

      1)i 必须是接口类型

    描述:
    具体类型实例的类型是静态的 在类型声明后就不再变化,所 具体类型的变量不存在类
    型查询 类型查询一定是对一个接口变量进行操作。也 就是说,上文中的i必须是接口变
    如果 是未初始 接口变量,则的值是nil 。
    var i io.Reader
    switch v := i.(type) { //此处i是为未初始化的接口变量,所以v为nil
        case nil : 
            fmt.Printf( " %T
     ”,v )  //<nil> 
        default : 
            fmt.Printf (”default”) 
    }

    ( 2 ) case 字句后面可 m~ 非接口类型名,也可以跟接口类型名,匹配是按照 case 子句的
    顺序进行的。

    如果 case 后面是一个接口类型名,且接口变量 绑定的实例类型实现了该接口类型的方法
    ,则匹配成功,v的类型是接口类型,v底层绑定的实例是 绑定具体类型实例的副本。

      示例:

    package main
    
    import (
        "io"
        "log"
        "os"
    )
    
    func main()  {
        f,err := os.OpenFile ("notes.txt",os.O_RDWR|os.O_CREATE, 0755)
        if err != nil {
            log.Fatal(err)
        }
        defer f.Close()
        var i io.Reader = f
        switch v :=i.(type) {
            //i的绑定的实例是*osFile类型,实现 io.ReadWriter接口,所以case匹配成功
            case io.ReadWriter:
                //v是io.ReadWriter 接口类型,所以可以调用Write方法
                v.Write( []byte ("io.ReadWriter
    " ))
            //由于上一个case 已经匹配,就算这个case 也匹配,也不会走到这里
            case *os.File:
                v.Write ([]byte ("*os.File
    "))
                v.Sync ()
            default:
            return
        }
    }
    如果case后面跟着多个类型,使用逗号分隔,接口变量i绑定的实例类型只要和其中一个类型匹配,
    则直接使用o赋值给 v,相当于v := o 这个语法有点奇怪,按理说编译器不应该允许这种操作,
    语言实现者可能想让 type switch 语句和普通的 sw itch 语句保持一样的语法规则,允许发生这种情况。
    package main
    
    import (
        "fmt"
        "io"
        "log"
        "os"
    )
    
    func main(){
        f,err := os.OpenFile("notes1.txt",os.O_RDWR|os.O_CREATE,0756)
        if err != nil {
            log.Fatal(err)
        }
        defer f.Close()
        var i  io.Reader = f
        switch v  := i.(type) {
        //多个类型,f满足其中任何一个就算匹配
        case *os.File,io.ReadWriter:
            //此时相当于执行v :=i ,v和i是等价的,使用v没有意义
            if v==i{
                fmt.Println(true) //true
            }
        default:
            return
        }
    }

    6 标准库的使用

      格式:

    switch i := i.(type) { 
    }

      类型查询和类型断言

    (1)类型查询和类型断言具有相同的语义,只是语法格式不同。 二者都能判断接口变量绑
    定的实例的具体类型,以及判断接口变量绑定的实例是否满足另一个接口类型。
    (2)类型查询使用 case 字句一次判断多个类型,类型断言一次只能判断一个类型,
    当然类型断言也可以使用 if else if 语句达到同样的效果

    7  接口优点和使用形式

      接口优点

    (1)解祸:复杂系统进行垂 和水平的分割是常用的设计手段,在层与层之间使用接口进
    行抽象和解辑是 种好的编程策略 Go 的非侵入式的接口使层与层之间的代码更加干净,
    具体类型和实现的接口之间不需要显式声明,增加了接口使用的自由度
    (2)实现泛型:由于现阶段Go语言还不支持泛型,使用空接口作为函数或方法参数能够用在需要泛型的场景中

      接口的使用形式

    3)作为结构 嵌字段。
    (2)作为函数或方法的形参。
    (3)作为函数或方法的返回值。
    (4)作为其他接口定义的嵌入宇段。

    三: 空接口

    概述:
    没有任何方法的接口,我们称之为空接 。空接口表示为 interface{}
    用途:
    空接口和泛型
    Go 语言没有泛型, 如果一个函数需要接收任意类型的参数, 则参数类型可以使用空接口,这是弥补没有泛型的一种手段
    //典型的就是 fmt 标准 里面的 print 函数
    func Fprint (w io.Writer, a . . . interface(}) (n int, err error) 
    空接口和反射
    空接口是反射实现 基础 反射库就是将相关具体的类型转换并赋值给空接 后才去处理,

    1 空接口和nil

    空接口不是真的为空,接口有类型和值两 概念

    示例

    package main
    import (
        "fmt"
    )
    type Inter interface {
        Ping()
        Pang()
    }
    type St struct {}
    
    func (St) Ping(){
        println("ping")
    }
    func (*St) Pang(){
        println("pang")     //pamg
    }
    func main(){
        var st *St = nil
        var it Inter = st
    
        fmt.Printf("%p
    ",st)  //0x0
    
        fmt.Printf("%p
    ",it)  //0x0
    
        if it !=nil {
            it.Pang()
            //下面的语句会导致 panic
            //方法转换为函数调用,第 一个参数是St类型,由于 St是nil ,无法获取指针所指的
            //对象佳,所以导致 panic
            //it.Ping
        }
    }

        comma-ok断言

    package main
    
    import (
        "fmt"
    )
    
    //空接口
    
    type Element interface {}
    
    type Person struct {
        name string
        age int
    }
    
    func main()  {
        //3容量的切片
        list := make([]Element,3)
        list[0] = 1            //int
        list[1]="Hello"      //string
        list[2] = Person{"zhangsan",18}
        for index,element := range list {
            //类型断言: value,ok =element,(T)
            if value,ok :=element.(int);ok {
                fmt.Printf("list[%d]是int类型,值是%d
    ",index,value)  //list[0]是int类型,值是1
            }else if value,ok := element.(string);ok {     
                fmt.Printf("list[%d]是string类型,值是%s
    ",index,value)   //list[1]是string类型,值是Hello
            }else {
                fmt.Printf("list[%d]是其他类型
    ",index)           //list[2]是其他类型
            }
        }
    }

      switch 接口测试

    package main
    
    import "fmt"
    //空接口
    type Element interface{}
    
    type Person struct {
        name string
        age  int
    }
    func main() {
        list := make([]Element, 3)
        list[0] = 1       //int
        list[1] = "Hello" //string
        list[2] = Person{"zhangsan", 18}
        for index,element := range list{
            switch value := element.(type) {
            case int :
                fmt.Printf("list[%d]是int类型,值是%d
    ",index,value)
            case string:
                fmt.Printf("list[%d]是string类型,值是%s
    ",index,value)
            default:
                fmt.Printf("list[%d]是其他类型
    ",index)
            }
        }
    }

    四: 接口的内部实现(这个涉及底层很多东西,我不会)

  • 相关阅读:
    struct tm->time() localtime() gmtime()
    解决VS2013中“This function or variable may be unsafe”的问题
    vm虚拟机三种网络模式
    vulnhub靶机-Raven解题思路
    vulnhub靶机-VulnOs:v2解题思路
    信息收集
    vulnhub靶机-SickOs1.2解题思路
    vulnhub靶机-SickOs解题思路
    vulnhub靶机-SkyTower解题思路
    vulnhub靶场-Stapler解题思路
  • 原文地址:https://www.cnblogs.com/liucsxiaoxiaobai/p/10806634.html
Copyright © 2020-2023  润新知