go語言中的結構體,是一種複合類型,有一組屬性構成,這些屬性被稱爲字段。結構體也是值類型,可使用new來建立。數組
定義:函數
type name struct { field1 type1 field2 type2 ... }
咱們能夠看到每個字段都由一個名字和一個類型構成,不過實際上,若是咱們若是不須要使用某個字段時,可使用」_」來代替它的名字學習
而且結構體字段能夠是任意類型,函數,接口,甚至是結構體自己均可以this
定義一個Person結構體指針
type Person struct { name string age int }
使用code
// 字面量形式初始化 var p1 = Person{ name: "Tom", age: 18, } var p2 = Person{"Cat", 20} fmt.Println(p1) //{Tom 18} fmt.Println(p1.name) // Tom fmt.Println(p2) // {Cat 20} p1.age = 20 // 設置p的age字段的值 fmt.Println(p1) // {Tom 20} 還可使用new函數來給一個結構體分配內存,並返回指向已分配內存的指針 var p3 *Person // 聲明一個Person類型的指針 p3 = new(Person) // 分配內存 // 上面兩句至關於 p3 := new(Person) p3.name = "Cat" p3.age = 10 fmt.Println(p3) // &{Cat 10} fmt.Println(p3.name) //
咱們能夠直接使用結構體指針經過」.」來訪問結構體的字段,就像直接使用結構體實例同樣, go會自動進行轉換對象
還有一種叫作混合字面量的語法來聲明,以下,這其實只是一種簡寫方式,底層仍是調用new方法繼承
var p4 = &Person{"Dog", 10} // 一樣返回的是Person類型的指針 fmt.Println(p4) // &{Dog 10} fmt.Println(p4.name) // Dog
go語言的結構體還支持匿名字段,也就是說一個只有類型而沒有字段名(連」_」都沒有)的字段,被匿名嵌入的也能夠是任何類型,此時類型名就是字段的名字,也就是咱們能夠直接使用類型名爲字段名來訪問匿名字段.接口
另外若是匿名字段是另外一個結構體,這就叫作內嵌結構體,這個特性能夠模擬相似繼承的行爲。內存
type Person struct { name string age int } type Student struct { Person int } // 定義一個Student類型的變量 var s = Student{Person{"gdb", 10}, 10} // 可使用以下的方法訪問內部結構體中的字段 fmt.Println(s.Person.name) // 也能夠這樣訪問,go將自動使用Person的name屬性,不過若是在Student中也定義了name字段,這裏就不能使用了 fmt.Println(s.name) // 訪問int類型的匿名字段,此時類型就是字段的名字 fmt.Println(s.int)
注意:這樣若是兩個字段有相同的名字時,外部的名字會覆蓋內部的;若是同一級別出現相同名字的字段,會出錯,須要注意;而且不能同時嵌⼊某⼀類型和其指針類型,由於它們名字相同。
結構體中的字段除了能夠有名稱和類型之外,還能夠有標籤。它是一個附屬於字段的字符串,能夠是文檔或其餘的重要標記。後面說反射時再說。
以前學習的面嚮對象語言,好比說Java, Python中,有類的概念,每一個類均可以有本身的成員變量,成員方法,它們都是定義在類中的
go語言中的結構體就相似與面嚮對象語言的類,而結構體的字段就至關於類中的成員變量,結構體也能夠有方法,可是不是直接定義在結構體中的,go語言中有一個接收者的概念,咱們能夠將函數做用在一個接收者,此時這個函數就被稱爲方法
接收者是某種類型的變量,不單單能夠是結構體,幾乎任何類型均可以是結構體,好比: int,bool, string或數組的別名類型,甚至能夠是函數類型,不過不能是接口類型
定義方法的示例:
type Person struct { name string age int } // 使用Person類型的實例作接收者,這就是一個Person類型方法,方法名前面括號中的就是接收者 func (this Person) getName() string { return this.name } // Peron類型的指針對象也能夠做爲接收者 func (this *Person) setName(name string) { this.name = name } tom := Person{"Tom", 20} fmt.Println(tom) // {Tom 20} fmt.Println(tom.getName()) // Tom tom.changeName("Bob") fmt.Println(tom) // {Bob 20}
這裏有一點須要注意:類型和綁定它的方法必須在同一個包中(不必定要在同一個文件中)
這裏使用類型直接做爲接收着 和 類型的指針做爲接收者的區別,就至關於普通函數中,值類型的參數和引用類型參數的區別;即在方法中對類型的實例的操做,不會影響外部的實例的值,而使用類型指針的實例做爲引用參數,在方法內部修改會影響外部的實例
咱們也可使用結構體內部的匿名字段,做爲方法的接收者,此時這個結構體,仍然能夠調用這個方法,此時編譯器會負責查找
type Person struct { name string age int } type Student struct { Person score int } func (p *Person) show() { fmt.Println("My name is " + p.name + ", I'm " + strconv.Itoa(p.age) + " years old") } tom := Person{"Tom", 20} // 調用匿名字段做爲接收器的方法 tom.show() // My name is Tom, I'm 20 years old
在此基礎上,咱們還能夠在結構體上,實現與匿名字段同名的方法,就像面向對象中的重寫相似,編譯器會先查找結構體實例做爲接收器的方法。
根據定義結構體以及方法的不一樣,方法集也有所不一樣,瞭解他們,對理解接口有幫助
type T struct { name string age int } type G struct { T action string } type S struct { *T sel string }