Go 語言的結(jié)構(gòu)體與方法
結(jié)構(gòu)體
結(jié)構(gòu)體是 go 語言中一個比較重要的概念,在 c 語言中也有類似的東西。由于他們沒有類的概念,結(jié)構(gòu)體可以簡單理解成類,是一個不同類型的數(shù)據(jù)構(gòu)成的一個集合。集合中不同類型的數(shù)據(jù)被稱為成員,每個成員都要自己不同的類型,可以理解為 js 中對象的每個屬性。
聲明結(jié)構(gòu)體
結(jié)構(gòu)體通過 type 和 struct 關(guān)鍵字進(jìn)行聲明,type 后接結(jié)構(gòu)體的名字,struct 后接結(jié)構(gòu)體每個成員的定義。
- type Person struct {
- name string
- age int
- gender string
- address string
- }
上面代碼有點類似于其他語言中接口的定義,實際上,go 也支持定義接口,我們只需要將 struct 關(guān)鍵字替換成 interface 就表示定義接口。
初始化結(jié)構(gòu)體
初始化結(jié)構(gòu)體有兩種方式,一種是通過字面量的方式,用結(jié)構(gòu)體名稱加上結(jié)構(gòu)體各個成員值的方式進(jìn)行初始化。用上面的 Person 結(jié)構(gòu)體舉例:
- var p = Person{"Shenfq", 25, "男", "湖南長沙"}
- fmt.Println("Person:", p)
這種方式需要每個值按照結(jié)構(gòu)體成員定義時的順序進(jìn)行初始化,當(dāng)然,也可以通過鍵值對的方式,打亂其順序。這種方式可以對部分成員進(jìn)行省略,省略的部分會根據(jù)其類型,取該類型的空值。
- var p = Person{
- name: "Shenfq",
- address: "湖南長沙",
- }
- fmt.Println("Person:", p)
- fmt.Println("Person.age:", p.age)
如果要訪問結(jié)構(gòu)體成員,可以通過 . 操作符,這與其他語言取對象屬性的方式一致。這里我們使用 p.age 的方式獲取了結(jié)構(gòu)體 p 的成員 age 的值。
除了字面量的方式初始化,結(jié)構(gòu)體還可以通過 new 關(guān)鍵字進(jìn)行初始化。
- var p = new(Person)
通過該方式初始化的結(jié)構(gòu)體有兩個特點:
- new 關(guān)鍵字返回的為結(jié)構(gòu)體指針;
- new 關(guān)鍵字返回的結(jié)果每個成員都是空值;
所以,我們通過 new 初始化結(jié)構(gòu)體的時候,取值的時候需要加 * 號。
- var p = new(Person)
- p.name = "Shenfq"
- p.age = 18
- p.gender = "男"
- p.address = "湖南長沙"
- fmt.Println("Person:", p)
如果直接在控制臺打印變量 p,會發(fā)現(xiàn)前面有個 &,表示這是一個指針。
匿名結(jié)構(gòu)體
結(jié)構(gòu)體和函數(shù)一樣也可以定義一個沒有名字的結(jié)構(gòu)體,就是在定義結(jié)構(gòu)體的同時進(jìn)行初始化,并且省略 type 關(guān)鍵字和結(jié)構(gòu)體名稱。
- var p = struct {
- name string
- age int
- gender string
- address string
- } { "Shenfq", 25, "男", "湖南長沙"}
方法
結(jié)構(gòu)體只能定義一個個成員,而且成員都是基礎(chǔ)類型,想要實現(xiàn)類似 OOP 中類的概念,還需要為結(jié)構(gòu)體提供方法。實際上,我們可以為結(jié)構(gòu)體指定方法,只需要在定義函數(shù)的函數(shù)名前面加上結(jié)構(gòu)體名,就能定義該函數(shù)為結(jié)構(gòu)體的方法。
我們?yōu)橹暗?Person 結(jié)構(gòu)體定義一個 sayHello 的方法。
- func (p Person) sayHello(name string) {
- fmt.Printf("Hi %s, I'm %s, How are you?\n", name, p.name)
- }
- p.sayHello("Jack")
調(diào)用結(jié)構(gòu)體方法的方式,和取結(jié)構(gòu)體成員的值一樣,也需要通過 . 操作符。
在 goland 的 Structure 中,能看到 Person 結(jié)構(gòu)體是包含 sayHello 方法的,說明方法的定義即使不在結(jié)構(gòu)體內(nèi),這個方法也是屬于該結(jié)構(gòu)體的。
方法中的指針
有時候,我們調(diào)用方法的同時,需要修改結(jié)構(gòu)體中一些成員的值,會發(fā)現(xiàn)原結(jié)構(gòu)體的值并沒有改變。
- func (p Person) growth() {
- p.age++
- }
- var p = Person{ age: 25 }
- p.growth()
上面的代碼中,我們定義的 growth 方法,會修改傳入結(jié)構(gòu)體中的 age 值。但是實際結(jié)果和我們預(yù)期的不一樣。
- var p = Person{ age: 25 }
- p.growth()
- fmt.Println("age:", p.age)
這是由于,傳入方法中的結(jié)構(gòu)體,是原結(jié)構(gòu)體復(fù)制后的值,需要修改原結(jié)構(gòu)體,就需要給方法傳入其指針。只需要在方法定義結(jié)構(gòu)體參數(shù)時,加上 * 號,表示變量 p 為結(jié)構(gòu)體指針。
- func (p *Person) growth() {
- p.age++
- }