Go 結構體(Struct)
引用曾經看到的一篇文章裡面對 Golang 中結構體的描述,如果說 Golang 的基礎型別是原子,那麼
結構體就是分子。我們都知道分子是由原子組成的,換言之就是結構體裡面可以包含基礎型別、切片、
字典、陣列以及結構體自身。
結構體型別的定義
結構體定義的一般方式
type identifier struct { field1 type1 field2 type2 ... }
結構體裡的欄位都有名字,而對於欄位在程式碼中從來也不被使用到,那麼可以命名它為_。對於相同型別的
欄位我們可以使用簡寫的形式,比如:
type T struct { a,b int }
還有一個需要注意的地方是結構體中欄位的大小寫,首字母大寫表示公有變數,首字母小寫表示私有變數,
相當於其他語言類成員變數的Public、Private ,但是這個公有變數和私有變數在 Go 中是相對於 Package 來
說的,在同一個 Package 都是公有的。
結構體變數的建立
1 最常見的建立結構體變數形式
package main import "fmt" type Circle struct { x int y int Radius int } func main() { var c Circle = Circle{ x:100, y:100, Radius:50, } fmt.Println(c) } -------------------------------- {100 100 50}
通過顯示指定結構體內部欄位的名稱和初始值初始化結構體,可以只指定部分欄位的初始值,或一個都不指定,
那些沒有被指定初始值的欄位會自動初始化為相應型別的零值。
2 結構體變數建立的第二種形式是不顯示指定欄位而是根據欄位順序初始化,但是這樣需要顯示的提供所有欄位的
初始值
package main import "fmt" type Circle struct { x int y int Radius int } func main() { var c Circle = Circle{100,100,50} fmt.Println(c) } ------------------------------- {100 100 50}
3 使用 new() 函式建立結構體變數
結構體變數和普通變數都有指標形式,使用取地址符就可以得到結構體的指標型別
package main import "fmt" type Circle struct { x int y int Radius int } func main() { var c *Circle = &Circle{100,100,50} fmt.Printf("%+v\n", c) } ------------------------------------------ &{x:100 y:100 Radius:50}
下面看一下使用 new() 建立一個零值結構體,所有的欄位都會被初始化成對應型別的零值
package main import "fmt" type Circle struct { x int y int Radius int } func main() { var c *Circle = new(Circle) fmt.Printf("%+v\n", c) } ----------------------------------- &{x:0 y:0 Radius:0}
注意:new 函式返回的是指標型別
結構體的引數傳遞
看一個例項
package main import "fmt" // 宣告一個結構體 type employee struct { name,address string height,weight float64 } // 定一個方法,該方法的引數是一個結構體,主要用於修改結構體成員中name的值 func modifyAttribute(emy employee) { emy.name = "newer" fmt.Println(emy) } func main() { // 初始化結構體 emy := employee{ name:"xiaoming", address:"beijing", height:172.0, weight:75.3, } // 列印修改前的值 fmt.Println(emy) // 呼叫modifyAttribute modifyAttribute(emy) // 列印修改後值 fmt.Println(emy) } ---------------------------------- 輸出結果 {xiaoming beijing 172 75.3} {newer beijing 172 75.3} {xiaoming beijing 172 75.3}
從上面的輸出結果上來看,雖然在 modifyAttribute 方法中修改了 name 值,但是在 main 函式中列印 name 的
值並沒有變化,說明這是一個值傳遞
我們把 modifyAttribute 函式的引數變成結構體的指標型別,如下
func modifyAttribute(emy *employee) { emy.name = "newer" fmt.Println(emy) } func main() { // 初始化結構體 emy := employee{ name:"xiaoming", address:"beijing", height:172.0, weight:75.3, } // 列印修改前的值 fmt.Println(emy) // 呼叫modifyAttribute modifyAttribute(&emy) // 列印修改後值 fmt.Println(emy) } ---------------------------- 輸出結果 {xiaoming beijing 172 75.3} &{newer beijing 172 75.3} {newer beijing 172 75.3}
我們看到在函式 modifyAttribute 中的修改影響到了 main 函式中的 name 值,這裡是引用傳遞
再看一個例子:編寫擴大圓半徑的函式
package main import "fmt" // 定義一個結構體 Circle type Circle struct { x int y int Radius int } // 通過值傳遞擴大圓半徑 func expandByValue(c Circle) { c.Radius *= 2 } // 通過引用傳遞擴大圓半徑 func expandByPointer(c *Circle) { c.Radius *= 2 } func main() { c := Circle{ Radius:50, } expandByValue(c) fmt.Println(c) expandByPointer(&c) fmt.Println(c) } -------------------------------- 輸出結果 {0 0 50} {0 0 100}
我們可以從上面的輸出中再次看到通過值傳遞,在函式裡面修改結構體的狀態不會影響原有結構
體的狀態,通過值傳遞就不一樣。
結構體方法
Go 語言不是面向物件的語言,在 Go 語言中沒有類的概念,結構體正是類的替代品。類可以附加很多成員方法,
結構體也可以。看一個例項,如何給結構體繫結方法:
package main import ( "fmt" "math" ) // 定義一個結構體 Circle type Circle struct { x int y int Radius int } // 計算圓的面積 第一個括號裡面表示的是方法的接收者 這裡方法的接收者是結構體 Circle // Area() 表示函式名 float64 表示函式的返回值型別 func (c Circle) Area() float64{ return math.Pi * float64(c.Radius) * float64(c.Radius) } // 計算圓的周長 func (c Circle) Circumference() float64 { return 2 *math.Pi * float64(c.Radius) } func main() { // 初始化結構體 c := Circle{ Radius:50, } fmt.Println(c.Area(),c.Circumference()) } ------------------------------------------------- 輸出結果 7853.981633974483 314.1592653589793
結構體的指標方法
如果使用結構體方法的形式給 Circle 增加一個擴大圓半徑的方法,會發現半徑還是擴大不了
func (c Circle) expand() { c.Radius *= 2 }
這個方法和前面的 expandByValue 函式是等價的,只是調整了一下第一個引數的位置,在引數傳遞的時候依然是值傳遞
,所以,還是無法起到擴大圓半徑的作用,這個時候就需要使用結構體的指標方法
func (c *Circle) expand() { c.Radius *= 2 }
結構體指標方法和值方法在呼叫上沒有區別,只是一個可以改變結構體內部狀態,另一個不會。另外指標方法可以使用結
構體變數呼叫,值方法也可以使用結構體指標變數使用
結構體變數呼叫指標方法(比如呼叫計算圓周長的方法):
package main import ( "fmt" "math" ) // 定義一個結構體 Circle type Circle struct { x int y int Radius int } // 計算圓的周長 func (c *Circle) Circumference() float64 { c.Radius *= 2 return 2 *math.Pi * float64(c.Radius) } func main() { // 初始化結構體 c := Circle{ Radius:50, } fmt.Println(c.Circumference()) } ----------------------------------------- 輸出結果 628.3185307179587
使用結構體指標變數呼叫值方法:
package main import ( "fmt" "math" ) // 定義一個結構體 Circle type Circle struct { x int y int Radius int } // 計算圓的面積 func (c Circle) Area() float64{ return math.Pi * float64(c.Radius) * float64(c.Radius) } func main() { // 初始化結構體 c := &Circle{ Radius:50, } fmt.Println(c.Area()) } --------------------------------- 輸出結果 7853.981633974483
內嵌結構體
結構體作為一種變數可以巢狀在另一個結構體中作為一個欄位使用,這種內嵌結構體在 Go 語言中稱之為
組合
package main import "fmt" // 定義一個結構體 type Pointer struct { x int y int } func (p Pointer) show() { fmt.Println(p.x,p.y) } // 定義另一個結構體 type Circle struct { // 將結構體 Pointer 巢狀在 Circle loc Pointer Radius int } func main() { c := Circle{ loc:Pointer{ x:100, y:100, }, Radius:50, } fmt.Println(c) fmt.Println(c.loc) fmt.Println(c.loc.x,c.loc.y) c.loc.show() } ----------------------------------- 輸出結果 {{100 100} 50} {100 100} 100 100 100 100
匿名內嵌結構體
還有一種特殊的內嵌結構體形式,內嵌的結構體不提供名稱。這時外面的結構體直接繼承內嵌結構體的所有
內部欄位和方法
package main import "fmt" // 定義一個結構體 type Pointer struct { x int y int } func (p Pointer) show() { fmt.Println(p.x,p.y) } // 定義另一個結構體 type Circle struct { // 匿名內嵌結構體 Pointer Radius int } func main() { c := Circle{ Pointer:Pointer{ x:100, y:100, }, Radius:50, } fmt.Println(c) fmt.Println(c.Pointer) fmt.Println(c.x,c.y) //繼承欄位 fmt.Println(c.Pointer.x,c.Pointer.y) c.show() // 繼承方法 c.Pointer.show() } -------------------------------------- 輸出結果 {{100 100} 50} {100 100} 100 100 100 100 100 100 100 100