整體來看,go語言中的面向對象在使用方式上是靈活易用的,能夠說設計理念真的很先進,讓人有一種如沐春風的感受。c++
若是你在學生時代經歷了一個從c到c++的學習歷程,你是否還記得,老師會說c++是面向對象的,因此咱們沒必要再使用c中的結構體做爲數據結構。咱們只需定義的是c++中的類,由於類中不僅有成員屬性,也有成員函數。換句話說, class是能夠完美替代struct的,並且更強大。數據結構
回到go中,咱們的面向對象使用的就是struct
,但時代不一樣了,此次咱們的struct也能夠有"成員函數"了。函數
定義一個典型的面向對象方式學習
package main import "fmt" type Human struct { height float32 weight int } func (h Human) BMIindex() (index int){ index = h.weight / int(h.height * h.height) return } func main() { person := Human{1.83, 75} fmt.Printf("this person's height is %.2f m\n", person.height) fmt.Printf("this person's weight is %d kg\n", person.weight) fmt.Printf("this person's BMI index is %d\n", person.BMIindex()) }
在main函數中咱們初始化了一個Human對象,並分別讀取了他們的屬性height和weight,最後調用了Human對象的成員函數BMIindex(),經過計算得到了這我的的"體質指數"。this
Receiver設計
上述例子中,一個Human對象的成員函數就是經過Receiver來定義的。咱們給一個普通的func添加了Receiver(就是上述示例中的h Human),就構成了一個method
BMIindex()。在這種狀況下,這個函數只能依賴於一個Human對象來起做用,而不能被獨立調用。其正確的調用方式就是上述的person.BMIindex()指針
下述又一個例子,咱們但願經過定義成員函數來改變對象的成員屬性。code
package main import "fmt" type Human struct { height float32 weight int } func (h Human) BMIindex() (index int){ index = h.weight / int(h.height * h.height) return } func (h Human) setHeight(height float32) { h.height = height } func main() { person := Human{1.83, 75} fmt.Printf("this person's height is %.2f m\n", person.height) fmt.Printf("this person's weight is %d kg\n", person.weight) fmt.Printf("this person's BMI index is %d\n", person.BMIindex()) person.setHeight(1.90) fmt.Printf("this person's height is %.2f m\n", person.height) } 輸出結果: this person's height is 1.83 m this person's weight is 75 kg this person's BMI index is 25 this person's height is 1.83 m
能夠看出,咱們調用person.setHeight(1.90)以後,person的height屬性並無改變爲1.90。而爲了解決這個問題,咱們須要改變receiver。咱們將setHeight()函數定義爲下述形式便可。對象
func (h *Human) BMIindex() (index int){ index = h.weight / int(h.height * h.height) return }
緣由:咱們將對象的指針類型
做爲receiver,才能改變其屬性的值。其本質爲,咱們能夠把receiver看做func的獨特的一個參數。若是傳遞的是一個對象類型,那麼函數中改變的其實是這個對象的副本;而若是傳遞一個指針類型,改變的纔是這個對象自己。繼承
關於receiver的選擇,能夠這樣理解,若是須要獲取對象屬性(get),則選用對象做爲receiver;若是須要改變對象屬性(set),則選取指針做爲receiver。
method的適用範圍
上述示例中,咱們定義的method都是對應於struct的,但實際上method的定義能夠依賴於全部的自定義類型
。所謂自定義類型,就是經過type語句給一些內置類型起了個"別名"後所定義的新類型。
package main import "fmt" type Sex string func (s *Sex) change(){ if *s == Sex("女") { *s = Sex("男") } } func main() { sex := Sex("女") fmt.Println("previous sex is ", sex) sex.change() fmt.Println("current sex is ", sex) }
這裏,咱們新定義了一個類型Sex,而且爲其定義了一個method change()。
面向對象中的繼承
package main import "fmt" type Human struct { height float32 weight int } type Woman struct { Human sex string } func (h Human) BMIindex() (index int){ index = h.weight / int(h.height * h.height) return } func main() { woman := Woman{Human{1.65, 50}, "女"} fmt.Printf("this woman's height is %.2f m\n", woman.height) fmt.Printf("this woman's wight is %d kg\n", woman.weight) fmt.Printf("this woman's BMIindex is %d\n", woman.BMIindex()) }
這個例子展示了Woman對Human的繼承。在Woman結構體中包含了匿名字段
Human,那麼Human中包含的屬性也是屬於一個Woman對象的屬性,Human對象的method一樣也能夠被Woman對象所使用。值得注意的是,這個method能夠被重寫
。只須要再定義一個以Woman對象類型爲receiver的BMIindex(),那麼再次調用woman.BMIindex時,實際調用的函數是新定義的這個函數。這就是method的重寫。
訪問屬性
若是你對面向對象中的訪問屬性很熟悉的話,你必定知道public、private和protected做爲訪問修飾符的做用。而在go語言中,咱們使用大小寫
來區分。
標識符首字母大寫,至關於public屬性。這樣的成員屬性或成員函數能夠被在包外部
被調用。例如上述Woman、BMIindex。
標識符首字母小寫,至關於protected屬性。這樣的成員屬性或成員函數只能在包內部
被使用。