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用法

相關文章
相關標籤/搜索