前面學習了 數組Array、切片Slice、字典Map類型,這三種類型都隻能存儲相同類型的值。結構體struct可以存儲不同類型的資料,結構體式不同類型的資料構成的資料集合。
Go語言中沒有Class類,是以結構體struct相當于其他語言中的Class。
聲明結構體
注意:結構體名首字母大寫其他包可以通路,首字母不是大寫的隻在本包内能通路到。
type 結構體名 struct {
成員名 成員類型
成員名 成員類型
...
成員名 成員類型
}
一旦定義了結構體它就可以被`執行個體化`,但寫法有講究。例如:
// 定義一個 student 的結構體
type student struct {
name string
age int
}
// 執行個體化結構體
// 正确1
// 如果不指定成員名稱,需要按照定義的順序依次寫出成員的值
var stu0 = student{"張三", 20}
fmt.Println("正确1", stu0)
// 錯誤
// 如果不指定成員名稱,沒有完全指定成員的值會報錯
// var stu1 = student{"錯誤"}
// fmt.Println(stu1)
// 正确2
// 指定成員名稱,給全部成員指派
stu2 := student{name: "李四", age: 18}
fmt.Println("正确2", stu2)
// 正确3
// 指定成員名稱,可以隻給部分成員指派,未指派成員将被賦對應類型的預設值
stu3 := student{name: "王五"}
fmt.Println("正确3", stu3)
// 正确4
// 隻定義一個student類型的變量,不指派成員,所有成員使用對應類型的預設值
var stu4 student
fmt.Println("正确4", stu4)
// 正确5
// 定義一個student類型的變量,指派部分成員,其他未指派成員使用對應類型的預設值
var stu5 student
stu5.age = 20
fmt.Println("正确5", stu5)
// 多行的錯誤寫法
// 當多行形式時,最後一行 `age:21` 和 `}` 不在一行會報錯
// var stu6 student = student{
// name: "老六",
// age: 21
// }
// 多行的正确寫法有兩種
// 第一種在`age:21`後加`,`
var stu6 student = student{
name: "老六",
age: 21,
}
fmt.Println("多行正确1", stu6)
// 第二種把`age:21` 和`}` 寫在一行
var stu7 student = student{
name: "老六",
age: 21 } // 隻要一行,中間有空格也行
fmt.Println("多行正确2", stu7)
通路結構體成員
實用 . 來通路:
結構體.成員名
// 例如
var stu5 student
stu5.age = 20
結構體作為函數參數
package main
import "fmt"
type student struct {
name string
age int
}
func main() {
var stu0 = student{"張三", 20}
// 傳遞student類型的參數
print(stu0)
}
// 參數類型是student的結構體
func print(s student) {
fmt.Println(s)
}
結構體指針
如果我們有一個指向結構體的指針 p,那麼可以通過 (*p).X 來通路其字段 X。不過這麼寫太啰嗦了,是以語言也允許我們使用隐式間接引用,直接寫 p.X 就可以。使用new()進行執行個體化操作時,會生成一個結構體指針變量。
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
p := &v
p.X = 10
fmt.Println(v)
(*p).X = 20
fmt.Println(v)
// 使用new()進行執行個體化操作時,會生成一個結構體指針變量。
v1 := new(Vertex)
(*v1).X = 66
fmt.Println(v1)
v1.X = 88
fmt.Println(v1)
}
結構體的方法
别人寫的文章中說 在 Go 語言中,我們無法在結構體内定義方法,必須使用組合函數的方式來定義結構體方法。組合函數的寫法如下:
package main
import "fmt"
// 定義 student 結構體
type student struct {
name string
age int
}
// 為結構體綁定方法:在函數名前面加括号,括号裡面定義一個結構體類型的變量
func (stu student) getName() string {
return stu.name
}
func main() {
// 執行個體化結構體 stu1
var stu1 student = student{
name: "張三",
age: 30,
}
// 因為 getName 方法已經綁定到 student 結構體中
// 是以 執行個體 stu1 可以直接調用 getName
name := stu1.getName()
fmt.Println(name)
}
為結構體綁定方法:在函數名前面加括号,括号裡面定義一個結構體類型的變量。
需要注意的是上面這種綁定方式,在方法内改變成員的值,沒有改變實際對象的值,就是我們平時說的值傳遞。如果要改變需要指針傳遞。
package main
import "fmt"
// 定義 student 結構體
type student struct {
name string
age int
}
// 為結構體綁定方法,值形式綁定
func (stu student) getName() {
stu.name = "zhangsan"
}
// 指針形式綁定
func (stu *student) changeName() {
stu.name = "王五"
}
func main() {
// 執行個體化結構體 stu1
var stu1 student = student{
name: "張三",
age: 30,
}
fmt.Println("初始值", stu1.name)
// 因為 getName 方法已經綁定到 student 結構體中
// 是以 執行個體 stu1 可以直接調用 getName
stu1.getName()
fmt.Println("值綁定", stu1.name)
// 這個方法是指針綁定
stu1.changeName()
fmt.Println("指針綁定", stu1.name)
}
結構體嵌套
結構體可以互相嵌套實作複雜,使用的“繼承”功能。
package main
import "fmt"
// 定義 student 結構體
type student struct {
name string
age int
}
type class struct {
className string
classLevel int
student // 匿名字段
}
func main() {
// 執行個體化結構體 stu1
var stu1 student = student{
name: "張三",
age: 30,
}
var class1 class = class{
className: "1年級",
classLevel: 1,
student: stu1,
}
fmt.Println(class1, class1.student.name)
}