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 排版


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM