Go基礎編程:結構體

結構體(struct)是自定義方式造成新的數據類型,結構體是類型中帶有成員的複合類型。Go 語言結構體是一種聚合的數據類型,是由零個或多個任意類型的值聚合成的實體。每一個值稱爲結構體的成員。來描述真實世界的實體和實體對應的各類屬性。編程

結構體屬性也叫字段成員,每一個字段都有名稱和類型,每一個名稱是惟一的。能夠是任何類型,如普通類型、複合類型、函數、map、interface、struct等,因此咱們能夠理解爲go語言中的「類」。json

定義

結構體定義方式以下:segmentfault

type name struct{
     fieldName1 type1
     fieldName2 type2
     ...
}

以下,定義User 結構體:函數

type User struct {
     Name string
     age  int
}

實例化測試

上面定義只是類型,就想是一個int同樣,須要定義一個類型變量纔可使用,相似Java的類。指針

直接定義變量使用code

package main
​
import (
    "fmt"
)
​
type User struct {
     Name string
     age  int
}
​
func main() {
     var user1 User //定義User 類型變量user
     var user2 *User //類型指針,未分配內存,不能直接使用
     fmt.Println(user1, user2) //{ 0} <nil>
}

定義默認成員變量orm

var user1 = User{Name: "abc"}
fmt.Println(user1)

func NewUser() *User {
    return &User{Name:"abc",age:20}
}

使用內建函數new()分配內存返回類型變量指針對象

var user = new(User)
fmt.Println(user) //&{ 0}

訪問成員繼承

使用.來訪問

var user User
user.Name = "abc"
user.age = 20
fmt.Println(user) //{abc 20}

首字母大小寫問題,成員大寫表示包外可見(即面向對象的公有屬性),小寫包外不可見

零值:結構體的零值是nil

初始值:結構體的初始值是非nil時,各成員對應類型的初始值

空結構體:空結構體就是沒有字段的結構體,空結構體不佔內存

package main
​
import (
     "fmt"
     "unsafe"
)
​
func main() {
     user1 := struct{}{}
     user2 := struct{}{}
     fmt.Printf("%p,%dn", &user1, unsafe.Sizeof(user1)) //0x585218,0
     fmt.Printf("%p,%dn", &user2, unsafe.Sizeof(user2)) //0x585218,0
}

從上面能夠看出空結構體內存地址和大小都是同樣的。根據這個特性,使用空結構體能夠做爲信號量,起到信號做用但不佔內存。如空結構體類型的chan

匿名結構體

匿名結構體沒有類型名稱,無須經過 type 關鍵字定義就能夠直接使用。

user := struct {
     Name string
 }{Name: "abc"}
fmt.Println(user) //{abc}

比較

若是結構體的所有成員都是能夠比較的,且成員的順序類型數量徹底同樣才能夠比較,兩個結構體將可使用==或!=運算符進行比較。

package main
​
import (
    "fmt"
)
​
func main() {
     user1 := struct {
     Name string
     }{Name: "abc"}
     user2 := struct {
     Name string
     }{Name: "abc"}
     fmt.Println(user1 == user2) //true
}

成員名稱不同

package main
​
import (
    "fmt"
)
​
func main() {
     user1 := struct {
     Name string
     }{Name: "abc"}
     user2 := struct {
     name string
     }{name: "abc"}
     fmt.Println(user1 == user2) //invalid operation: user1 == user2 (mismatched types struct { Name string } and struct { name string })
}

成員數量不同

package main
​
import (
     "fmt"
)
​
func main() {
     user1 := struct {
     Name string
     }{Name: "abc"}
     user2 := struct {
     Name string
     age  int
     }{Name: "abc"}
     fmt.Println(user1 == user2) //invalid operation: user1 == user2 (mismatched types struct { Name string } and struct { Name string; age int })
}

成員類型不能比較

package main
​
import (
    "fmt"
)
​
func main() {
     user1 := struct {
     Name string
     m    map[int]int
     }{Name: "abc"}
     user2 := struct {
     Name string
     m    map[int]int
     }{Name: "abc"}
     fmt.Println(user1 == user2) //invalid operation: user1 == user2 (struct containing map[int]int cannot be compared)
}

順序不同

package main
​
import (
     "fmt"
)
​
func main() {
     user1 := struct {
     Name string
     age  int
     }{Name: "abc"}
     user2 := struct {
     age  int
     Name string
     }{Name: "abc"}
     fmt.Println(user1 == user2) //invalid operation: user1 == user2 (mismatched types struct { Name string; age int } and struct { age int; Name string })
}

其實整個結構體就是一個類型(如int),成員順序、類型這些不同,總體的結構體就不同,故對於強類型語言來講就是不能比較的,對應類型徹底同樣還須要注意成員是不是能夠比較,如slice、map等

Go語言沒有面向對象這個概念,但能夠把結構體看作是一個類,能夠實現面向對象的特性,如經過組合和嵌入實現繼承

匿名字段

匿名字段是結構體沒有顯示的名字,是結構體嵌入一個或多個結構體,以下面

B直接嵌入A ,B是匿名字段

package main
​
import (
    "fmt"
)
​
type A struct {
     Name string
     B
}
type B struct {
     Age int
     Name string
}

訪問成員變量

func main() {
     var a = A{Name:"a",B:B{Name:"b",Age:20}}
     fmt.Printf("%#vn", a) //main.A{Name:"", B:main.B{Age:0}}
     fmt.Println(a.Name)  //a
     fmt.Println(a.B.Name)  //b
     fmt.Println(a.Age)  //20 
}

只有一個成員名稱的狀況下,Go語法糖能夠省略嵌入結構體

fmt.Println(a.B.Age) //20
fmt.Println(a.Age)   //20

對應有多個相同名稱的成員,不能省略,由於編譯器不知道是哪一個

type C struct {
     A
     B
}
​
func main() {
     var c = C{A:A{Name:"a"},B:B{Name:"b",Age:20}}
     fmt.Println(c.Name) //ambiguous selector c.Name
}

正確作法是

func main() {
     var c = C{A:A{Name:"a"},B:B{Name:"b",Age:20}}
     fmt.Println(c.A.Name) //a
     fmt.Println(c.B.Name) //b
}

組合

上面是沒有名字的嵌入結構體,還能夠給嵌入結構體命名,訪問必需要帶上具體的字段,不能省略。

package main
​
import (
     "fmt"
)
​
type A struct {
     Btype B
}
type B struct {
     Age int
     Name string
}
​
func main() {
     var a = A{Btype:B{Name:"b",Age:20}}
     fmt.Println(a.Name) //.Name undefined (type A has no field or method Name)
}

標籤

以下面在字段後面用`` 包起來的是標籤,主要是經過反射來序列化和反序列化,具體由反射章節來說。

type User struct {
 Id int `json:"id"`
 Account string `json:"account" form:"account"`
 Nickname string `gorm:"nickname" json:"nickname" form:"nickname"`
}

方法

方法通常都是面向對象編程(OOP)的一個特性,Go語言的方法其實與一個值或變量關聯的特殊的函數。這個值或變量叫作接收者

func ([typeName] 接收者) name (param) [return]{}

接收者是自定義的類型

package main
​
import (
    "fmt"
)
​
type A struct {} //結構體
​
type B int  //int
​
func (a A) show()  {
    fmt.Println("a............")
}
​
func (b B) show()  {
     fmt.Println("b............")
}
​
func main() {
     var a  A
     var b  B
     a.show()
     b.show()
}

接收者不能直接用內置類型

func (c int) show()  {  //cannot define new methods on non-local type int
    fmt.Println("b............")
}

接收者能夠是值類型或指針類型

package main
​
import (
     "fmt"
)
​
type A struct {}
​
type B struct {}
​
func (a A) show()  { //值類型
     fmt.Println("a............")
}
​
func (b *B) show()  { //指針類型
     fmt.Println("b............")
}
​
func main() {
     var a  A
     var b  B
     a.show()
     b.show()
}

對與B來講,下面兩種調用方式是等價的,本質上他們都是同樣的,b.show()的寫法是省略了(&b),只不過由語法糖來補全

func main() {
     var b  B
     b.show()
     (&b).show()
}

方法能夠訪問接收者自身的信息,以下

package main
​
import (
    "fmt"
)
​
type User struct {
     Id int 
     Account string 
     Nickname string 
}
​
func (u User)show()  {
    fmt.Println(u.Nickname)
}
​
func main() {
     var a  = User{Nickname:"測試"} 
     a.show() //測試
}

值類型接收者拷貝類型的所有,修改不會影響原數據;指針拷貝的是地址,修改影響原數據

package main
​
import (
    "fmt"
)
​
type User struct {
     Id int
     Account string
     Nickname string
}
​
func (u User)show()  {
    fmt.Println(u)
}
func (u User)setName1()  {
     u.Nickname="值類型"
}
​
func (u *User)setName2()  {
     u.Nickname="指針類型"
}
​
func main() {
     var a  = User{Nickname:"測試"}
     a.setName1()
     a.show()
     a.setName2()
     a.show()
}

接受者類型自己不能爲指針

package main
​
import (
    "fmt"
)
​
type A int 
​
type B *int  //變量類型爲指針
​
func (a A) show()  {
     fmt.Println("a............")
}
​
func (b B) show()  {  //invalid receiver type B (B is a pointer type)
     fmt.Println("b............")
}

方法的參數和返回值這些和函數同樣,具體看函數章節

相關文章
相關標籤/搜索