15. 理解 Go 语言面向对象编程:结构体与继承

Hi,你们好,我是明哥。git

在本身学习 Golang 的这段时间里,我写了详细的学习笔记放在个人我的微信公众号 《Go编程时光》,对于 Go 语言,我也算是个初学者,所以写的东西应该会比较适合刚接触的同窗,若是你也是刚学习 Go 语言,不防关注一下,一块儿学习,一块儿成长。github

个人在线博客:http://golang.iswbm.com
个人 Github:github.com/iswbm/GolangCodingTimegolang


0. 什么是结构体?

在以前学过的数据类型中,数组与切片,只能存储同一类型的变量。若要存储多个类型的变量,就须要用到结构体,它是将多个容易类型的命令变量组合在一块儿的聚合数据类型。编程

每一个变量都成为该结构体的成员变量。数组

能够理解为 Go语言 的结构体struct和其余语言的class有相等的地位,可是Go语言放弃大量面向对象的特性,全部的Go语言类型除了指针类型外,均可以有本身的方法,提升了可扩展性。微信

在 Go 语言中没有没有 class 类的概念,只有 struct 结构体的概念,所以也没有继承,本篇文章,带你学习一下结构体相关的内容。函数

1. 定义结构体

声明结构体性能

type 结构体名 struct {
    属性名   属性类型
    属性名   属性类型
    ...
}

好比我要定义一个能够存储我的资料名为 Profile 的结构体,能够这么写学习

type Profile struct {
    name   string
    age    int
    gender string
    mother *Profile // 指针
    father *Profile // 指针
}

2. 定义方法

在 Go 语言中,咱们没法在结构体内定义方法,那如何给一个结构体定义方法呢,答案是可使用组合函数的方式来定义结构体方法。它和普通函数的定义方式有些不同,好比下面这个方法云计算

func (person Profile) FmtProfile() {
	fmt.Printf("名字:%s\n", person.name)
	fmt.Printf("年龄:%d\n", person.age)
	fmt.Printf("性别:%s\n", person.gender)
}

其中fmt_profile 是方法名,而(person Profile) :表示将 fmt_profile 方法与 Profile 的实例绑定。咱们把 Profile 称为方法的接收者,而 person 表示实例自己,它至关于 Python 中的 self,在方法内可使用 person.属性名 的方法来访问实例属性。

完整代码以下:

package main

import "fmt"

// 定义一个名为Profile 的结构体
type Profile struct {
	name   string
	age    int
	gender string
	mother *Profile // 指针
	father *Profile // 指针
}

// 定义一个与 Profile 的绑定的方法
func (person Profile) FmtProfile() {
	fmt.Printf("名字:%s\n", person.name)
	fmt.Printf("年龄:%d\n", person.age)
	fmt.Printf("性别:%s\n", person.gender)
}

func main() {
    // 实例化
	myself := Profile{name: "小明", age: 24, gender: "male"}
    // 调用函数
	myself.FmtProfile()
}

输出以下

名字:小明
年龄:24
性别:male

3. 方法的参数传递方式

上面定义方法的方式叫当你想要在方法内改变实例的属性的时候,必须使用指针作为方法的接收者。

package main

import "fmt"

// 声明一个 Profile 的结构体
type Profile struct {
	name   string
	age    int
	gender string
	mother *Profile // 指针
	father *Profile // 指针
}

// 重点在于这个星号: *
func (person *Profile) increase_age() {
	person.age += 1
}

func main() {
	myself := Profile{name: "小明", age: 24, gender: "male"}
	fmt.Printf("当前年龄:%d\n", myself.age)
	myself.increase_age()
	fmt.Printf("当前年龄:%d", myself.age)
}

输出结果 以下,能够看到在方法内部对 age 的修改已经生效。你能够尝试去掉 *,使用值作为方法接收者,看看age是否会发生改变。

当前年龄:24
当前年龄:25

至此,咱们知道了两种定义方法的方式:

  • 以值作为方法接收者
  • 以指针作为方法接收者

那咱们如何进行选择呢?如下几种状况,应当直接使用指针作为方法的接收者。

  1. 你须要在方法内部改变结构体内容的时候
  2. 出于性能的问题,当结构体过大的时候

有些状况下,以值或指针作为接收者均可以,可是考虑到代码一致性,建议都使用指针作为接收者。

无论你使用哪一种方法定义方法,指针实例对象、值实例对象均可以直接调用,而没有什么约束。这一点Go语言作得很是好。

4. 结构体实现 “继承”

为何标题的继承,加了双引号,由于Go 语言自己并不支持继承。

但咱们可使用组合的方法,实现相似继承的效果。

在生活中,组合的例子很是多,好比一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一块儿,最后才有了咱们用的电脑。

一样的,在 Go 语言中,把一个结构体嵌入到另外一个结构体的方法,称之为组合。

如今这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。

type company struct {
	companyName string
	companyAddr string
}

type staff struct {
	name string
	age int
	gender string
	position string
}

若要将公司信息与公司职员关联起来,通常都会想到将 company 结构体的内容照抄到 staff 里。

type staff struct {
	name string
	age int
	gender string
    companyName string
	companyAddr string
	position string
}

虽然在实现上并无什么问题,但在你对同一公司的多个staff初始化的时候,都得重复初始化相同的公司信息,这作得并很差,借鉴继承的思想,咱们能够将公司的属性都“继承”过来。

可是在 Go 中没有类的概念,只有组合,你能够将 company 这个 结构体嵌入到 staff 中,作为 staff 的一个匿名字段,staff 就直接拥有了 company 的全部属性了。

type staff struct {
	name string
	age int
	gender string
	position string
	company   // 匿名字段 
}

来写个完整的程序验证一下。

package main

import "fmt"

type company struct {
	companyName string
	companyAddr string
}

type staff struct {
	name string
	age int
	gender string
	position string
	company
}

func main()  {
	myCom := company{
		companyName: "Tencent",
		companyAddr: "深圳市南山区",
	}
	staffInfo := staff{
		name:     "小明",
		age:      28,
		gender:   "男",
		position: "云计算开发工程师",
		company: myCom,
	}

	fmt.Printf("%s 在 %s 工做\n", staffInfo.name, staffInfo.companyName)
	fmt.Printf("%s 在 %s 工做\n", staffInfo.name, staffInfo.company.companyName)
}

输出结果以下,可见staffInfo.companyNamestaffInfo.company.companyName 的效果是同样的。

小明 在 Tencent 工做
小明 在 Tencent 工做

5. 内部方法与外部方法

在 Go 语言中,函数名的首字母大小写很是重要,它被来实现控制对方法的访问权限。

  • 当方法的首字母为大写时,这个方法对于全部包都是Public,其余包能够随意调用
  • 当方法的首字母为小写时,这个方法是Private,其余包是没法访问的。

系列导读

01. 开发环境的搭建(Goland & VS Code)

02. 学习五种变量建立的方法

03. 详解数据类型:****整形与浮点型

04. 详解数据类型:byte、rune与string

05. 详解数据类型:数组与切片

06. 详解数据类型:字典与布尔类型

07. 详解数据类型:指针

08. 面向对象编程:结构体与继承

09. 一篇文章理解 Go 里的函数

10. Go语言流程控制:if-else 条件语句

11. Go语言流程控制:switch-case 选择语句

12. Go语言流程控制:for 循环语句

13. Go语言流程控制:goto 无条件跳转

14. Go语言流程控制:defer 延迟调用

15. 面向对象编程:接口与多态

16. 关键字:make 和 new 的区别?

17. 一篇文章理解 Go 里的语句块与做用域

18. 学习 Go 协程:goroutine

19. 学习 Go 协程:详解信道/通道

20. 几个信道死锁经典错误案例详解

21. 学习 Go 协程:WaitGroup

22. 学习 Go 协程:互斥锁和读写锁

23. Go 里的异常处理:panic 和 recover

24. 超详细解读 Go Modules 前世此生及入门使用

25. Go 语言中关于包导入必学的 8 个知识点

26. 如何开源本身写的模块给别人用?

27. 说说 Go 语言中的类型断言?

28. 这五点带你理解Go语言的select用法

相关文章
相关标签/搜索