一文读懂 Go 类型嵌套

94 阅读2分钟

struct 是 Go 中常用的数据类型,可以用来表示任何想要的数据,在一些组合的场景中,我们会把一个类型、一个 struct 嵌入到其他的 struct 里面,这里就是所说的类型嵌套,但是并不是所有的类型都可以嵌套,这里有两个基本规则

  • 一个类型 T 可以被内嵌,除非它表示一个命名的指针类型,或者基类型是指针、接口的指针类型
  • 指针类型 *T (T 是一个基类型为指针的类型)可以内嵌,除非它表示一个指针或者接口类型
func main() {
    type Encoder interface{ Encode([]byte) []byte }
    type Person struct {
       name string
       age  int
    }
    type Alias = struct {
       name string
       age  int
    }
    type AliasPtr = *struct {
       name string
       age  int
    }
    type IntPtr *int
    type AliasPP = *IntPtr

    type Peng struct {

       // 以下类型可以被内嵌
       Encoder
       Person
       //*Person
       Alias
       //*Alias
       AliasPtr
       int
       //*int
    }

    type Hong struct {

       // 以下类型不可以被内嵌
       AliasPP           // 基类型是指针
       *Encoder          // 基类型是接口
       *AliasPtr         // 基类型是指针
       IntPtr            // 命名指针类型
       *IntPtr           // 基类型是指针
       *chan int         // 未命名类型
       struct{ age int } // 未命名类型
       map[string]int    // 未命名类型
       []int64           // 未命名类型
       func()
    }
}

此外,一个类型也不能和基类型同时出现,比如 int 和 *int 就不能同时内嵌,同理,一个 struct 也不能内嵌它本身

类型内嵌的最大意义其实是扩展了方法的使用范围,当类型内嵌以后,嵌入类型就拥有了被嵌入类型的方法,在其他面向对象的语言中,大都是通过继承来实现类似的功能,但 Go 是基于组合来做的

package main

type A struct {
	FieldX int
}

func (a A) MethodA() {}

type B struct {
	*A
}

type C struct {
	B
}

func main() {
	var c = &C{B: B{A: &A{FieldX: 5}}}

	_ = c.B.A.FieldX
	_ = c.B.FieldX
	_ = c.A.FieldX // A is a promoted field of C
	_ = c.FieldX   // FieldX is a promoted field

	c.B.A.MethodA()
	c.B.MethodA()
	c.A.MethodA()
	c.MethodA() // MethodA is a promoted method of C
}

在类型内嵌里面,有一个类型提升的概念,就是内嵌类型拥有了被内嵌类型的所有属性,并且可以直接引用,但是类型提升会带来另一个类型碰撞的问题,假设 struct A 拥有 B C两个类型,然后 B 拥有一个 X变量,C 拥有一个 X 方法,这时候如果使用 A.X 就会编译不过,因为你根本就不知道这个 X 指代什么