1. 程式人生 > >golang | Go語言入門教程——結構體初始化與繼承

golang | Go語言入門教程——結構體初始化與繼承

本文始發於個人公眾號:**TechFlow**,原創不易,求個關注

今天是golang專題第10篇文章,我們繼續來看golang當中的面向物件部分。

在上一篇文章當中我們一起學習了怎麼建立一個結構體,以及怎麼給結構體定義函式,還有函式接收者的使用。今天我們來學習一下結構體本身的一些使用方法。

初始化

在golang當中結構體初始化的方法有四種。

new關鍵字

我們可以通過new關鍵字來建立一個結構體的例項,這種方法和其他語言比較類似,這樣會得到一個空結構體指標,當中所有的欄位全部填充它型別對應的零值。比如int就對應0,float對應0.0,如果是其他結構體則對應nil。

type Point struct {
 x int
 y int
}

func main() {
 var p *Point = new(Point)
 fmt.Print(p)
}

從這段程式碼當中我們可以看到,new函式返回的是一個結構體指標,而不是結構體的值。一般我們很少用new關鍵字,而是直接通過結構體加花括號的方式來初始化。

結構體名稱

相比於使用new關鍵字,我們更常用的是通過結構體名稱加上花括號的方式來進行初始化。

如果我們不再花括號當中填寫引數的話,那麼同樣會得到一個填充了零值的結構體。結構體當中的所有屬性都會被賦予這個型別對應的零值。

type Point struct {
 x int
 y int
}

func main() {
    p := Point{}
 fmt.Print(p)
}

如果我們想要初始化一個結構體的指標,我們只需要在結構體名稱之前加上取地址符&即可。所以建立一個結構體指標可以這樣:

func main() {
    p := &Point{}
 fmt.Print(p)
}

golang當中取地址符和宣告指標的關鍵字和C語言是一樣的,對於熟悉C語言的同學來說,這應該並不困難。

我們在花括號當中填充引數,這些引數會按照順序填充到結構體的屬性當中。為了防止混淆,我們也可以在值之前加上它對應的屬性名稱。

func main() {
    p := &Point{0, 0}
    k := &Point{x: 0, y: 10}
 fmt.Print(p)
}

繼承

很多人不喜歡golang的主要原因就是覺得golang閹割了面向物件的很多功能之後,導致開發的時候束手束腳,總覺得不太方便。其中為人詬病得比較厲害的就是繼承,覺得golang當中沒有繼承,寫有依賴的結構體的時候非常蛋疼。

我之前一度也這麼覺得,最近仔細研究了其中的道道之後,發現我錯了,golang當中也是有繼承的,不過它實現的方式和我們一般理解上的不太一樣,有一些出其不意。所以我們拿正統的眼光去看它總會覺得它不倫不類,哪裡不太對勁。這種感覺有點像是武俠小說里名門正派看旁門左派的感覺,但旁門左派並不代表就不行,也有能打的。

在我們正常的映像當中,我們實現繼承就應該是標明當前這個類的父類是哪個類,這樣底層編譯器自動將父類的屬性和方法都拷貝一份到子類當中來。加上private、public等關鍵詞束縛,來控制一下什麼方法和屬性可以被繼承什麼不可以就完美了。

我們用Python舉個例子,Python當中對於繼承的定義已經非常簡潔了,實現起來大概是這樣的:

class A:
    pass

class B(A):
    pass

直接在類名的後面就加上繼承的資訊,實際上絕大多數主流語言也都是這麼幹的。但golang不是,它做了一件什麼事呢?它將父類作為變數定義在了子類的裡面,嚴格說起來這已經不是繼承了,算是一種奇怪的組合,但它起到的功能類似於繼承。

我光說理解起來很累,我們來看個例子,比如我們當下有一個父類(結構體),它有兩個結構體方法:

type Father struct {
    Name string
}

func(entity Father) Hello() {...}
func(entity Father) World() {...}

現在我們要建立一個它的子類,需要把Father這個結構體填進去,變成其中一個成員變數。

type Child struct {
    Father
    ...
}

那有了這麼一個看起來很奇怪的子類之後,我們怎麼呼叫父類的方法呢?

答案是直接呼叫。

child := Child{}
child.Hello()

按照我們的理解,由於父類是子類當中的一個成員,所以我們想要呼叫父類的方法,應該寫成child.Father.Hello()才對。但實際上golang替我們做了相關的優化,我們直接呼叫方法,也可以找到父類當中的方法。

如果我們要改寫父類的方法也不困難,我們可以這樣操作:

func (entity Child) World() {
    entity.Father.World()
    ...
}

如此,父類當中的World方法就被Child改寫了,這樣就完成了繼承當中對父類函式的改寫。

總結

到這裡,關於golang當中結構體初始化與繼承的介紹就結束了。不知道大家看完這篇有什麼樣的感受,我最大的感覺是好像沒有第一次看到它的時候那麼難以接受了XD。

據說這個設計和C++當中的虛基類的概念非常接近,但是虛基類非常難以理解(比如我就沒能理解),以至於許多C++工程師會自動忽略它的存在。相比之下,golang的這種設計要容易理解得多。雖然看起來麻煩,但是理解起來也並不困難。

今天的文章到這裡就結束了,如果喜歡本文的話,請來一波素質三連,給我一點支援吧(關注、轉發、點贊)。

本文使用 mdnice 排版

![](https://user-gold-cdn.xitu.io/2020/7/12/17342ac9e710ba4a?w=258&h=258&f=png&