typora/note/Go/结构体类型嵌入.md
2024-12-12 10:48:55 +08:00

3.2 KiB
Raw Blame History

多个嵌入方式

  • 接口嵌入
  • 结构体嵌入

嵌入总结

  • 结构体类型的方法集合包含嵌入的接口类型的方法集合
  • 当结构体类型 T 包含嵌入字段 E 时,*T 的方法集合不仅包含类型 E 的方法集合,还要包含类型 *E 的方法集合
  • 基于非接口类型的 defined 类型创建的新 defined 类型不会继承原类型的方法集合
  • 通过类型别名定义的新类型则和原类型拥有相同的方法集合

接口嵌入

  • 接口就是一个方法集合
  • 新接口类型(如接口类型 I将嵌入的接口类型如接口类型 E的方法集合并入到自己的方法集合中
  • 在 Go 1.14 版本之前是有约束的:如果新接口类型嵌入了多个接口类型,这些嵌入的接口类型的方法集合不能有交集,同时嵌入的接口类型的方法集合中的方法名字,也不能与新接口中的其他方法同名
type I interface {
    M1()
    M2()
    M3()
}


type I interface {
    E
    M3()
}
  • 以某个类型名、类型的指针类型名或接口类型名直接作为结构体字段的方式就叫做结构体的类型嵌入这些字段也被叫做嵌入字段Embedded Field
  • 结构体可以嵌入结构体和接口类型
  • 嵌入字段类型的底层类型不能为指针类型
  • 嵌入字段的名字在结构体定义也必须是唯一的,如果两个类型的名字相同,它们无法同时作为嵌入字段放到同一个结构体定义中

结构体嵌入

type T1 int
type t2 struct{
    n int
    m int
}

type I interface {
    M1()
}

type S1 struct {
    T1
    *t2
    I            
    a int
    b string
}

嵌入后方法集合

  • 结构体类型的方法集合,包含嵌入的接口类型的方法集合

type I interface {
    M1()
    M2()
}

type T struct {
    I
}

func (T) M3() {}

func main() {
    var t T
    var p *T
    dumpMethodSet(t)
    dumpMethodSet(p)
}

main.T's method set:
- M1
- M2
- M3

*main.T's method set:
- M1
- M2
- M3
  • 嵌入了其他类型的结构体类型本身是一个代理
  • 调用其实例所代理的方法时Go 会首先查看结构体自身是否实现了该方法
  • 如果实现方法了Go 就会优先使用结构体自己实现的方法
  • 如果没有实现,那么 Go 就会查找结构体中的嵌入字段的方法集合中,是否包含了这个方法
  • 如果多个嵌入字段的方法集合中都包含这个方法那么方法集合存在交集。Go 编译器就会因无法确定究竟使用哪个方法而报错
  • 去掉因嵌入造成的交集的方法或者结构体自身实现交集的方法
  type E1 interface {
      M1()
      M2()
      M3()
  }
  
  type E2 interface {
     M1()
     M2()
     M4()
 }
 
 type T struct {
     E1
     E2
 }
 
 func main() {
     t := T{}
     t.M1()
     t.M2()
 }

打印一个interface的方法集合

func dumpMethodSet(i interface{}) {
   dynTyp := reflect.TypeOf(i)

   if dynTyp == nil {
   	fmt.Printf("there is no dynamic type\n")
   	return
   }

   n := dynTyp.NumMethod()
   if n == 0 {
   	fmt.Printf("%s's method set is empty!\n", dynTyp)
   	return
   }

   fmt.Printf("%s's method set:\n", dynTyp)
   for j := 0; j < n; j++ {
   	fmt.Println("-", dynTyp.Method(j).Name)
   }
   fmt.Printf("\n")
}