文章目录

  • 1、前言
  • 2、结构体的定义与声明和使用
  • 2.1、结构体的定义
  • 2.2、结构体的声明
  • 2.3、结构体的使用
  • 2.3.1、函数参数之传递值拷贝
  • 2.3.2、函数参数之传递指针拷贝
  • 2.3.3、结构体的方法
  • 2.3.3.1、以值为接收体
  • 2.3.3.2、以指针为接收体
  • 3、结构体的嵌套
  • 3.1、入门案例
  • 3.2、需要注意的点


1、前言

  • 在 Go 语言中不存在 Class 类这种概念,但是存在 struct 结构体,可以代替实现 Class 的功能,并且 Go 语言中也不存在继承,但是可以通过结构体嵌套实现类似 Java 中的继承。

2、结构体的定义与声明和使用

2.1、结构体的定义

type Hero struct {
	bloodVolume uint16
	name string
	age uint8
}

2.2、结构体的声明

  • 方式一:通过内置方法 new,得到一个指向结构体的指针。
var hero = new(Hero)
hero.bloodVolume = 100
hero.name = "项羽"
hero.age = 27
  • 方式二:通过取地址符 “&” 直接得到一个指向结构体的指针。
hero := &Hero{
	bloodVolume: 100,
	name:        "嬴政",
	age:         27,
}
  • 方式三:最一般的方式,获得的不是指针。
hero := Hero{
	bloodVolume: 100,
	name:        "嬴政",
	age:         27,
}

2.3、结构体的使用

2.3.1、函数参数之传递值拷贝
type Hero struct {
	BloodVolume uint16
	Name string
	Age uint8
}

func Method1(h Hero) {
	h.Age++
	fmt.Println("method1.", h)
}

func main() {
	hero := Struct.Hero{
		BloodVolume: 100,
		Name:        "嬴政",
		Age:         27,
	}
	fmt.Println("main.", hero)
	Struct.Method1(hero)
	fmt.Println("main.", hero)
}
  • 执行以上程序,输出结果如下:
main. {100 嬴政 27}
method1. {100 嬴政 28}
main. {100 嬴政 27}
  • 可见,当传入的结构体是值拷贝时,在函数内修改结构体的值,不会对原来结构体造成影响。
2.3.2、函数参数之传递指针拷贝
func Method2(h *Hero) {
	(*h).Age++
	fmt.Println("method2.", *h)
}

func main() {
	hero := Struct.Hero{
		BloodVolume: 100,
		Name:        "嬴政",
		Age:         27,
	}
	fmt.Println("main.", hero)
	Struct.Method2(&hero)
	fmt.Println("main.", hero)
}
  • 执行以上程序,输出结果如下:
main. {100 嬴政 27}
method2. {100 嬴政 28}
main. {100 嬴政 28}
  • 可见,当传入的结构体是指针拷贝时,在函数内修改结构体的值,会对原来结构体造成影响。
2.3.3、结构体的方法
2.3.3.1、以值为接收体
func (h Hero) SetBloodVolume(v uint16) {
	h.BloodVolume = v
}

func main() {
	hero := Struct.Hero{
		BloodVolume: 100,
		Name:        "嬴政",
		Age:         27,
	}
	hero.SetBloodVolume(90)
	fmt.Println(hero)
}
  • 执行结果如下:
{100 嬴政 27}
  • 发现以值为接收体,在方法内对结构体对象进行修改,不会对原本结构体对象造成影响,因为地址都不一样啊。
2.3.3.2、以指针为接收体
func (h *Hero) SetBloodVolume2(v uint16) {
	h.BloodVolume = v
	fmt.Printf("SetBloodVolume2.%p\n", h)
}

func main() {
	hero := Struct.Hero{
		BloodVolume: 100,
		Name:        "嬴政",
		Age:         27,
	}
	hero.SetBloodVolume2(90)
	fmt.Printf("main.%p\n", &hero)
	fmt.Println(hero)
}
  • 输出结果如下:
SetBloodVolume2.0xc0000044a0
main.0xc0000044a0
{90 嬴政 27}
  • 发现以指针为接收体,在方法内对结构体对象进行修改,会对原本结构体对象造成影响,因为地址一样啊。

3、结构体的嵌套

3.1、入门案例

  • 在 Java 中,子类继承父类,子类就可以获得父类定义的方法和属性,虽然在 Go 语言中,不存在继承这个概念,但是在 Go 中通过嵌套,姑且称被嵌套的结构体为基结构体,嵌套其他结构体的结构体就叫派生结构体吧,这样派生结构体就“继承”了基结构体的属性和方法,看示例代码:
type Creature struct {
	Kind string
}

type Animal struct {
	Creature
}

type Dog struct {
	Animal
}

func (c Creature) Breath() {
	fmt.Println("会呼吸")
}

func TestInherit() {
	d := Dog{Animal{Creature{Kind: "生物"}}}
	fmt.Println(d.Kind)
	d.Breath()
}
  • 这里我来了个套娃,Dog 套 Animal,Animal 套 Creature,尽管这样,Dog 依然能调用 Creature 的方法,并且能访问它的属性 Kind,执行结果如下:
生物
会呼吸

3.2、需要注意的点

  • 虽然派生结构体可以调用基结构体的方法,访问它的属性,但它并不是严格意义上的继承,在 Java 中,子类继承了父类,那么可以让“父类 = 子类”,但是 Go 中不可以,可以看以下示例代码:
func Method1(c Creature) {
}

func main() {
	d := Dog{Animal{Creature{Kind: "生物"}}}
	Method1(d) //编译阶段报错:cannot use d (type Dog) as type Creature in argument to Method1
}
  • 包括像下面这段代码,更是不能被允许!
func Method2(d Dog) {
}

func main() {
	c := Interface.Creature{Kind: "生物"}
	Interface.Method2(c) //编译阶段报错:cannot use c (type Creature) as type Dog in argument to Method2
}