Go接口 interface

Go接口 interface

Go接口 interface

声明

type People interface {
    Speak(string) string
  //method(parama)(return param)
}
//实现接口
type Stduent struct{}
func (stu *Stduent) Speak(think string) (talk string) {
    if think == "love" {
        talk = "You are a good boy"
    } else {
        talk = "hi"
    }
    return
}

func main() {
    var peo People = Stduent{}//编译出错,需要改为&Student{} People为interface类型,就是指针类型 
    think := "love"
    fmt.Println(peo.Speak(think))
}

嵌套

type Tank interface {
    Walk()
    Fire()
}
type Plane interface {
    Fly()
}
//类似于对象中的接口继承特性
type tankPlane interface {
    Tank
    Plane
}

接口的内部构造

package main

import (
    "fmt"
)

type People interface {
    Show()
}

type Student struct{}

func (stu *Student) Show() {

}

func live() People {
    var stu *Student
    return stu
}
//最终输出BBBB
func main() {
    if live() == nil {
        fmt.Println("AAAAAAA")
    } else {
        fmt.Println("BBBBBBB")
    }
}

interface在使用的过程中,共有两种表现形式

一种为空接口(empty interface),定义如下:

var MyInterface interface{}

另一种为非空接口(non-empty interface), 定义如下:

type MyInterface interface {
        function()
}

这两种interface类型分别用两种struct表示,空接口为eface, 非空接口为iface.

image-20211024203358377

空接口eface

空接口eface结构,由两个属性构成,一个是类型信息_type,一个是数据信息。其数据结构声明如下:

type eface struct {      //空接口
    _type *_type         //类型信息
    data  unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}

_type属性:是GO语言中所有类型的公共描述,Go语言几乎所有的数据结构都可以抽象成 _type,是所有类型的公共描述,type负责决定data应该如何解释和操作,type的结构代码如下:

type _type struct {
    size       uintptr  //类型大小
    ptrdata    uintptr  //前缀持有所有指针的内存大小
    hash       uint32   //数据hash值
    tflag      tflag
    align      uint8    //对齐
    fieldalign uint8    //嵌入结构体时的对齐
    kind       uint8    //kind 有些枚举值kind等于0是无效的
    alg        *typeAlg //函数指针数组,类型实现的所有方法
    gcdata    *byte
    str       nameOff
    ptrToThis typeOff
}

data属性: 表示指向具体的实例数据的指针,他是一个unsafe.Pointer类型,相当于一个C的万能指针void*

image-20211024203625680

非空接口iface

iface 表示 non-empty interface 的数据结构,非空接口初始化的过程就是初始化一个iface类型的结构,其中data的作用同eface的相同

type iface struct {
  tab  *itab
  data unsafe.Pointer
}

iface结构中最重要的是itab结构(结构如下),每一个 itab 都占 32 字节的空间。itab可以理解为pair<interface type, concrete type> 。itab里面包含了interface的一些关键信息,比如method的具体实现。

type itab struct {
  inter  *interfacetype   // 接口自身的元信息
  _type  *_type           // 具体类型的元信息
  link   *itab
  bad    int32
  hash   int32            // _type里也有一个同样的hash,此处多放一个是为了方便运行接口断言
  fun    [1]uintptr       // 函数指针,指向具体类型所实现的方法
}
  1. interface type包含了一些关于interface本身的信息,比如package path,包含的method。这里的interfacetype是定义interface的一种抽象表示。
  2. type表示具体化的类型,与eface的 type类型相同。
  3. hash字段其实是对_type.hash的拷贝,它会在interface的实例化时,用于快速判断目标类型和接口中的类型是否一致。另,Go的interface的Duck-typing机制也是依赖这个字段来实现。
  4. fun字段其实是一个动态大小的数组,虽然声明时是固定大小为1,但在使用时会直接通过fun指针获取其中的数据,并且不会检查数组的边界,所以该数组中保存的元素数量是不确定的。

image-20211024204329046

所以,People拥有一个Show方法的,属于非空接口,People的内部定义应该是一个iface结构体

type People interface {
    Show()  
}  

image-20211024204624679

func live() People {
    var stu *Student
    return stu      
}    

stu是一个指向nil的空指针,但是最后return stu 会触发匿名变量 People = stu值拷贝动作,所以最后live()放回给上层的是一个People insterface{}类型,也就是一个iface struct{}类型。 stu为nil,只是iface中的data 为nil而已。 但是iface struct{}本身并不为nil.

image-20211024204734844

所以之前的判断结果为BBBBBBB

func Foo(x interface{}) {
    if x == nil {
        fmt.Println("empty interface")
        return
    }
    fmt.Println("non-empty interface")
}
func main() {
    var p *int = nil
    Foo(p)
}

结果为non-empty interface

不难看出,Foo()的形参x interface{}是一个空接口类型eface struct{}

image-20211024205132484

在执行Foo(p)的时候,触发x interface{} = p语句,所以此时 x结构如下。

image-20211024205200117

所以 x 结构体本身不为nil,而是data指针指向的p为nil。

inteface{}与*interface{}

type S struct {
}

func f(x interface{}) {
}

func g(x *interface{}) {
}

func main() {
    s := S{}
    p := &s
    f(s) //A
    g(s) //B
    f(p) //C
    g(p) //D
  //B D会报错
}

Golang是强类型语言,interface是所有golang类型的父类 函数中func f(x interface{})interface{}可以支持传入golang的任何类型,包括指针,但是函数func g(x *interface{})只能接受*interface{}


   转载规则


《Go接口 interface》 朱林刚 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Go 内存四区 Go 内存四区
内存四区内存四区概念:A.数据类型本质:​ 固定内存大小的别名 B. 数据类型的作用:​ 编译器预算对象(变量)分配的内存空间大小。 C.内存4区 流程说明 1、操作系统把物理硬盘代码load到内存 2、操作系统把c代码分成四
2021-11-18
下一篇 
Go 排序 Go 排序
排序对整数,浮点数,字符串切片排序对于[]int, []float, []string这种元素类型是基础类型的切片使用sort包提供的下面几个函数进行排序。 具体实现见sort.Interface的IntSlice和Float64Slice
2021-11-18
  目录