结构体(struct)嵌套(实现类似继承的效果)

  Go语言只有组合,没有继承,不能完全用面向对象的思维来理解组合。

  Go语言只有组合,没有继承,不能完全用面向对象的思维来理解组合。

  Go语言只有组合,没有继承,不能完全用面向对象的思维来理解组合。



package main

import "fmt"

// Human 定义一个名为Human的结构体
type Human struct {
   Name string // 姓名
   Age  int    // 年龄
   Sex  string // 性别
}

// Intro Human结构体的自我介绍方法
func (h *Human) Intro() {
   fmt.Printf("俺叫%v%v),今年%v岁。\n", (*h).Name, (*h).Sex, (*h).Age)
}

// Programmer 定义一个名为Programmer的结构体,该结构体将嵌入Human结构体
type Programmer struct {
   Human             // 嵌入Human结构体,此时Programmer结构体就能获得了Human结构体的所有字段和方法
   Language  string  // 所使用的编程语言
   WorkYears float64 // 工作年限
}

// Intro Programmer结构体的自我介绍方法
// 重要说明:Go语言只有组合,该方法不会覆盖Human的同名方法,所以Programmer既有自己的Intro()方法,同时也有Human的Intro()方法,
//      不要用面向对象的思维来理解这段代码,在Go语言里这叫做方法提升,在main()函数里会演示如何分别调用两个Intro()方法。
func (p *Programmer) Intro() {
   fmt.Printf(
      "俺叫%v%v),今年%v岁,职业是%v程序猿,工作年限%v年。\n",
      (*p).Name, (*p).Sex, (*p).Age, (*p).Language, (*p).WorkYears,
   )
}

func main() {
   programmer := new(Programmer)

   (*programmer).Name = "张三"
   (*programmer).Age = 18
   (*programmer).Sex = "男"
   (*programmer).Language = "PHP"
   (*programmer).WorkYears = 3.14

   // 调用Programmer自身的Intro()方法
   (*programmer).Intro() // 俺叫张三(男),今年18岁,职业是PHP程序猿,工作年限3.14年。

   // 调用Human的Intro()方法
   (*programmer).Human.Intro() // 俺叫张三(男),今年18岁。
}



  在很多地方我们可以看到“package.struct”写法的嵌套,这并没有什么特殊的,只是由于跨包嵌套需要指定包名而已,跟同包嵌套没有本质不同。

package main

import (
   "fmt"
   "time"
)

type MyTime struct {
   time.Time // 嵌入标准库的time包的Time结构体
}

func (mt *MyTime) Location() string {
   return "Asia/Shanghai"
}

func main() {
   mt := &MyTime{}

   // 调用自身的Location()方法
   fmt.Printf("mt.Location() is %+v \n", mt.Location()) // mt.Location() is Asia/Shanghai

   // 调用time.Time的Location()方法
   fmt.Printf("mt.Time.Location() is %+v \n", mt.Time.Location()) // mt.Time.Location() is UTC
}



  再举个例子来证明Go语言只有组合,没有继承。

package main

import "fmt"

type People struct{}

func (p *People) ShowA() {
   fmt.Println("People.ShowA()")
   p.ShowB()
}

func (p *People) ShowB() {
   fmt.Println("People.ShowB()")
}

type Teacher struct {
   People // Teacher嵌入了People,所以Teacher获得了People的全部字段和方法
}

// ShowB 重要说明:Teacher.ShowB()没有覆盖People.ShowB()。
func (t *Teacher) ShowB() {
   fmt.Println("Teacher.ShowB()")
}

func main() {
   t := Teacher{}
   t.ShowA()
   // ========== 输出结果 ========== //
   // People.ShowA()
   // People.ShowB()
}

// ========== 说明 ========== //
// 1、上面的代码对于使用面向对象语言的开发者可能无法理解,其实只要记住一件事,那么一切都会变得非常容易理解,
//    那就是Go语言只有组合,没有继承,既然没有继承那就不存在方法覆盖,即Teacher.ShowB()没有覆盖People.ShowB()。
// 2、这里也可以从另一个角度来理解,那就是Go语言里没有隐式转换,既然ShowA()方法声明了p的数据类型是*People,
//    那么p就肯定是*People类型变量,其中不可能发生隐式转换,从而p.ShowB()肯定是调用People.ShowB()方法。



  关于匿名嵌入和具名嵌入

package main

import "fmt"

type Human struct {
   Name  string // 姓名
   Birth int    // 出生
   Sex   string // 性别
}

func (h *Human) Intro() {
   fmt.Printf("俺叫%v%v),出生于%v年。\n", (*h).Name, (*h).Sex, (*h).Birth)
}

type Teacher struct {
   Human // 匿名嵌入Human结构体
}

type Programmer struct {
   Human Human // 具名嵌入Human结构体(并且是同名)
}

func main() {
   teacher := &Teacher{}
   programmer := &Programmer{}

   // 【推荐写法】
   (*teacher).Name = "张三"
   (*teacher).Birth = 2003
   (*teacher).Sex = "男"
   (*teacher).Intro() // 俺叫张三(男),出生于2003年。

   // 【不推荐写法】
   // 不推荐原因:Human是被匿名嵌入Teacher,如果加上“.Human”容易误解是被具名嵌入
   (*teacher).Human.Name = "李四"
   (*teacher).Human.Birth = 2004
   (*teacher).Human.Sex = "女"
   (*teacher).Human.Intro() // 俺叫李四(女),出生于2004年。

   // 具名嵌入只有一种写法,不存在推不推荐
   (*programmer).Human.Name = "王五"
   (*programmer).Human.Birth = 2005
   (*programmer).Human.Sex = "男"
   (*programmer).Human.Intro() // 俺叫王五(男),出生于2005年。
}

// ========== 说明 ========== //
// 1、一般来说嵌入结构体使用匿名的方式即可,除非有特殊需求才使用具名嵌入。

Copyright © 2024 码农人生. All Rights Reserved