golang interface底层实现,使用场景

题目来源:腾讯

答案1:

interface 底层结构

根据 interface 是否包含有 method,底层实现上用两种 struct 来表示:ifaceefaceeface表示不含 methodinterface 结构,或者叫 empty interface。对于 Go 中的大部分数据类型都可以抽象出来 _type 结构,同时针对不同的类型还会有一些其他信息。

  1. type eface struct {
  2. _type *_type
  3. data unsafe.Pointer
  4. }
  5. type _type struct {
  6. size uintptr // type size
  7. ptrdata uintptr // size of memory prefix holding all pointers
  8. hash uint32 // hash of type; avoids computation in hash tables
  9. tflag tflag // extra type information flags
  10. align uint8 // alignment of variable with this type
  11. fieldalign uint8 // alignment of struct field with this type
  12. kind uint8 // enumeration for C
  13. alg *typeAlg // algorithm table
  14. gcdata *byte // garbage collection data
  15. str nameOff // string form
  16. ptrToThis typeOff // type for pointer to this type, may be zero
  17. }

iface 表示 non-empty interface 的底层实现。相比于 empty interfacenon-empty 要包含一些 methodmethod 的具体实现存放在 itab.fun 变量里。

  1. type iface struct { tab *itab data unsafe.Pointer } // layout of Itab known to compilers // allocated in non-garbage-collected memory // Needs to be in sync with // ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs. type itab struct { inter *interfacetype _type *_type link *itab bad int32 inhash int32 // has this itab been added to hash? fun [1]uintptr // variable sized }

interface的使用要满足2个条件才有意义:

  1. 实现了interface的几个struct是相似关系(比如docker和kvm都是虚拟机)、平级的,并且输入输出参数完全一致。(这点是interface的本质,能实现interface的肯定是满足这个条件)
  2. 在业务逻辑上,调用实现interface的struct是不确定的,是通过某种方式传递进来,而不是顺序的业务逻辑,比如structA、structB、structC如果是有顺序的则是错误的,下面这样是错误的:
  1. func main() { var i interfaceX i = &structA{...} i.Add() i = &structB{...} i.Add() i = &structC{...} i.Add()}

这样逻辑是正确的:

  1. var i interfaceXswitch opt {case "A": i = &structA{}case "B": i = &structB{}case "C": i = &structC{}}i.Add()i.Del()

就是说调用者对于实现interface的struct是根据某个参数(通过API传递过来,或者配置文件传递过来,或者etcd传递过来)来选择某个struct,这种逻辑才适用interface。而如果程序逻辑是被调用者依次执行,则不适用interface。

总结适用interface的调用者业务逻辑(伪代码):

  1. type I interface { ...}var i Iswitch opt { //opt通过某种方式传递进来,而不是写死case "A": i = &structA{...}case "B": i = &structB{...}case "C": i = &structC{...}default: errors.New("not support")}