golang中並無明確的面向對象的說法,實在要扯上的話,能夠將struct比做其它語言中的class。php
type Poem struct { Title string Author string intro string }
這樣就聲明瞭一個類,其中沒有public、protected、private的的聲明。golang用另一種作法來實現屬性的訪問權限:屬性的開頭字母是大寫的則在其它包中能夠被訪問,不然只能在本包中訪問。類的聲明和方法亦是如此。java
func (poem *Poem) publish() { fmt.Println("poem publish") }
或者golang
func (poem Poem) publish() { fmt.Println("poem publish") }
和其它語言不同,golang聲明方法和普通方法一致,只是在func後增長了poem Poem這樣的聲明。加和沒有加*的區別在於一個是傳遞指針對象,一個是傳遞值對象。數組
type T struct { Name string } func (t T) M1() { t.Name = "name1" } func (t *T) M2() { t.Name = "name2" }
M1() 的接收者是值類型 T, M2() 的接收者是值類型 *T , 兩個方法內都是改變Name值。函數
下面聲明一個 T 類型的變量,並調用 M1() 和 M2() 。ui
t1 := T{"t1"} fmt.Println("M1調用前:", t1.Name) t1.M1() fmt.Println("M1調用後:", t1.Name) fmt.Println("M2調用前:", t1.Name) t1.M2() fmt.Println("M2調用後:", t1.Name)
輸出結果爲:this
M1調用前: t1
M1調用後: t1
M2調用前: t1
M2調用後: name2指針
下面猜想一下go會怎麼處理。code
先來約定一下:接收者能夠看做是函數的第一個參數,即這樣的: func M1(t T), func M2(t *T)。 go不是面向對象的語言,因此用那種看起來像面向對象的語法來理解可能有誤差。對象
當調用 t1.M1() 時至關於 M1(t1) ,實參和行參都是類型 T,能夠接受。此時在M1()中的t只是t1的值拷貝,因此M1()的修改影響不到t1。
當調用 t1.M2() => M2(t1),這是將 T 類型傳給了 *T 類型,go可能會取 t1 的地址傳進去: M2(&t1)。因此 M2() 的修改能夠影響 t1 。
p := struct { Name string Gender string Age uint8 }{"Robert", "Male", 33}
匿名結構體最大的用處是在內部臨時建立一個結構以封裝數據,而沒必要正式爲其聲明相關規則。
實例化對象有好幾種方式
poem := &Poem{} poem.Author = "Heine" poem2 := &Poem{Author: "Heine"} poem3 := new(Poem) poem3.Author = "Heine" poem4 := Poem{} poem4.Author = "Heine" poem5 := Poem{Author: "Heine"}
實例化的時候能夠初始化屬性值,若是沒有指明則默認爲系統默認值
注意:
p1 := &Poem{ "zhangsan", 25, []string{"lisi", "wangwu"}, }
使用中若是包含數組,結構體的實例化須要加上類型如上若是intro的類型是[]string。
加&符號和new的是指針對象,沒有的則是值對象,這點和php、java不一致,在傳遞對象的時候要根據實際狀況來決定是要傳遞指針仍是值。
tips:當對象比較小的時候傳遞指針並不划算。
func NewPoem(param string, p ...interface{}) *Poem
示例:
func NewPoem(author string) (poem *Poem) { poem = &Poem{} poem.Author = author return } poem6 := NewPoem("Heine")
確切的說golang中叫作組合(composition)
func (e *Poem) ShowTitle() { fmt.Printf(e.Title) } type Poem struct { Title string Author string intro string } type ProsePoem struct { Poem Author string }
ProsePoem屬性中聲明瞭Poem,表示組合了Poem的屬性和方法(屬性和方法都會被繼承)。
prosePoem := &ProsePoem{} prosePoem.author = "Heine"
prosePoem := &ProsePoem{ Poem: Poem{ Title: "Jack", Author: "slow", intro: "simple", }, Author: "test", }
若是其中屬性有衝突,則之外圍的爲主,也就是說會被覆蓋。
type ProsePoem struct { Poem Author string }
當訪問Author的時候默認爲ProsePoem的Author,若是須要訪問Poem的Author屬性可使用
prosePoem.Poem.Author來訪問方法同理。
prosePoem := &ProsePoem{} prosePoem.Author = "Shelley" prosePoem.Poem.Author = "Heine" fmt.Println(prosePoem)
從輸出中能夠很直觀看到這一點。
&{{ Heine } Shelley}
方法的繼承和屬性一致,這裏再也不羅列。經過組合的話能夠很好的實現多繼承。
好比有一個父親,是中國人:
type Father struct { MingZi string } func (this *Father) Say() string { return "你們好,我叫 " + this.MingZi }
能夠理解爲父親類有一個屬性,有一個Say方法
有父親固然有母親,母親是個外國人:
type Mother struct { Name string } func (this *Mother) Say() string { return "Hello, my name is " + this.Name }
父親和母親結合有了孩子類,孩子類繼承了父親和母親:
type Child struct { Father Mother }
而後孩子類有一個實例c:
c := new(Child) c.MingZi = "張小明" c.Name = "Tom Zhang"
由於MingZi和Name這個屬性在Mother和Father中並無衝突,因此能夠直接使用 c. 就能夠獲取而沒有問題
可是,若是這樣直接調用Child類的Say方式:
c.Say()
會出現衝突:
ambiguous selector c.Say
怎麼辦?其實這樣就能夠輕鬆解決:
c.Father.Say() c.Mother.Say()
上面兩條表達式的值分別爲:
你們好,我叫 張小明
Hello, my name is Tom Zhang
方法重載就是一個類中能夠有相同的函數名稱,可是它們的參數是不一致的,在java、C++中這種作法廣泛存在。golang中若是嘗試這麼作會報從新聲明(redeclared)錯誤,可是golang的函數能夠聲明不定參數,這個很是強大。
func (poem *Poem) recite(v ...interface{}) {
fmt.Println(v)
}
其中v …interface{}表示參數不定的意思,其中v是slice類型,fmt.Println方法也是這樣定義的。若是要根據不一樣的參數實現不一樣的功能,要在方法內檢測傳遞的參數。