golang 結構體
一個結構體型別可以包含若干個欄位,也可以不包含任何欄位。空結構體可以關聯上一些方法,從而看成是函式的特殊版本。
demo1:
// AnimalCategory 代表動物分類學中的基本分類法 type AnimalCategorystruct { kingdom string// 界 phylum string// 門 classstirng// 綱 orderstring// 目 familystring// 科 genusstring// 屬 speciesstring// 種 } function (ac AnimalCategory) String() string { return fmt.Sprintf("%s%s%s%s%s%s", ac.kingdom ,ac.phylum ,ac.class,ac.order, ac.family,ac.genus,ac.species) }
使用時:
category := AnimalCategory{species: "cat"} fmt.Printf("The animal category :%s\n", category )
在Go語言中,我們可以通過一個型別編寫名為string 的方法,來自定義該型別的字串表示形式。這個string 方法不需要任何引數宣告,但需要有一個string 型別的結果宣告。
demo2:
type Animal struct { scientficName string // 學名 AnimalCategory// 動物基本分類 } func (a Aminal) string { }
demo2聲明瞭一個結構體型別,名叫Animal 。它又兩個欄位。一個是string型別的欄位scientficName ,代表了動物的學名。而另一個欄位宣告中只有AnimalCategory,它正是我再前面編寫的那個結構體型別的名字。
AnimalCategor代表了什麼?
答:欄位宣告AnimalCategor代表了Animal 型別的一個嵌入欄位。Go語言規範規定,如果一個欄位的宣告中只有欄位的型別名而沒有欄位的名稱,那麼它就是一個嵌入欄位,也可以被稱為匿名欄位 。我們可以通過此型別變數的的名稱後跟“.”,然後再跟嵌入欄位型別的方式引用到該欄位。也就是說,嵌入欄位的型別既是型別也是名稱。
animals := Animal { scientficName: "American Shorthair", AnimalCategory :category , } fmt.Printf("The animal : %s\n", animal)
fmt.Printf("The animal : %s\n", animal)呼叫的是animal的string方法。因為嵌入欄位category 的string()方法被遮蔽了。注意,只要名稱相同,無論這兩個方法的簽名是否一致,被嵌入型別的方法都會遮蔽掉嵌入欄位吧的同名方法。
但我們可以通過鏈式的選擇表示式,選擇嵌入欄位的欄位或方法:
func (a Aminal) string { return fmt.Sprintf("%s (category:%s)", a.scientficName,a.AnimalCategory ) }
問: Go語言是用嵌入欄位實現了繼承嗎?
答:Go語言沒有繼承的概念。
簡單來說,面向物件程式設計中的繼承,其實是通過犧牲一定的程式碼簡潔性來換取可擴充套件性,而且這種可擴充套件性是通過侵入的方式來實現的。型別之間的組合採用的是非宣告的方式,我們不需要顯式地宣告某個型別實現了某個介面,或者一個型別繼承了另一個型別。
同時,型別組合也是非侵入式的,它不會破壞型別的封裝或加重型別之間的耦合。我們要做的只是把型別當做欄位嵌入進來,然後坐享其成地使用嵌入欄位所擁有的一切。如果嵌入欄位哪裡不和心意,我們還可以用"包裝""或“”遮蔽“”的方式來調整和優化。
另外,型別間的組合也是靈活的,我麼總是可以通過嵌入欄位的方式把一個型別的屬性和能力“嫁接”給另一個型別。
這時候,被嵌入型別也就自然而然地實現了嵌入欄位所實現的介面。再者,組合要比繼承更加簡潔和清晰,Go語言可以輕而易舉地通過嵌入多個欄位來實現強大的型別,卻不會有多重繼承那樣複雜的層次結構和可觀的管理成本。
問: 值方法跟指標方法有什麼區別?
答:1. 值方法的接受者是該方法所屬的那個型別值的一個副本。我們在該方法內對該副本的修改一般都不會體現在原值上,除非這個型別本身是某個引用型別(切片,字典)的別名型別。
而指標方法的接受者,是該方法所屬的那個基本型別值的指標值的一個副本。我們在這樣的方法內對該副本指向的值進行修改,卻一定會體現在原值上。
2.一個自定義資料型別的方法集合中僅會包含它的所有值方法,而該型別的指標型別的方法卻囊括了前者的所有方法,包括所有值方法和所有指標方法。
嚴格來說,我們在這樣的基本型別的值上只能呼叫到它的值方法。但是,Go語言會適時地為我們進行自動地轉譯,使得我們在這樣的值上也能呼叫到它的指標方法。
func (cat *Cat) SetName(name string) { cat.Name = name }
當我們呼叫cat.SetName("monster"),Go語言自動把它轉譯成了(&cat).SetName("monster"),即:先去cat的指標值,然後在該指標上呼叫SetName方法。
3.一個型別的方法集合中有哪些方法與它能實現哪些介面型別是息息相關的。如果一個基本型別和它的指標型別的方法集合是不一樣的,那麼它們具體實現的介面型別的數量就會有差異,除非這兩個數量都為零。
比如,一個指標型別實現了某某介面型別,但它的基本型別卻不一定能夠作為該介面的實現型別。
go語言什麼時候會出現轉譯?
在基本值上呼叫它的指標方法時Go會先對基本值進行取址再呼叫它的指標方法。