包羅永珍的結構體 -- 就要學習 Go 語言

Seekload發表於2019-01-22

Go 語言的陣列可以儲存一組相同型別的資料,而結構體可以將不同型別的變數資料組合在一起,每一個變數都是結構體的成員。

建立並初始化一個結構體

可以使用下面的語法建立一個結構體:

type StructName struct{
    field1 fieldType1
    field2 fieldType2
}
複製程式碼

建立一個含有 firstNamelastNamesalaryfullTime 成員變數的結構體 Employee

type Empolyee struct{
	firstName string
	lastName string
	salary int
	fullTime bool
}
複製程式碼

相同型別的成員變數可以放在一行,所以,上面的程式碼可以簡寫成:

type Empolyee struct{
	firstName,lastName string
	salary int
	fullTime bool
}
複製程式碼

使用型別別名 Employee 建立一個結構體變數 ross

var ross Empolyee
ross.firstName = "ross"
ross.lastName = "Bingo"
ross.salary = 1000
ross.fullTime = true
fmt.Println(ross)
複製程式碼

輸出:

{ross Bingo 1000 true}
複製程式碼

上面的程式碼建立了結構體變數 ross,併為每一個成員變數賦值。使用.訪問結構體的成員。
還可以使用字面量的方式初始化結構體:

1、方式一
ross := Empolyee{
	"ross",
	"Bingo",
	1000,
	true,
}
輸出:{ross Bingo 1000 true}

2、方式二
ross := Empolyee{
	lastName:"Bingo",
	firstName:"ross",
	salary:1000,
}
輸出:{ross Bingo 1000 false}
複製程式碼

方式一,初始化時省略了成員變數名稱,但是必須按順序地將給出所有的成員的值。必須記住所有成員的型別且按順序賦值,這給開發人員帶來了額外的負擔且程式碼的維護性差,一般不採用這種方式;
提倡採用方式二,不用關心成員變數的順序,給需要初始化的成員賦值,未賦值的成員預設就是型別對應的零值。注意:方式一和方式二初始化方式不可以混用

ross := Empolyee{
	firstName:"ross",
	lastName:"Bingo",
	1000,
	fullTime:true,
}
複製程式碼

編譯出錯:mixture of field:value and value initializers

成員變數的順序對於結構體的同一性很重要,如果將上面的 firstNamelastName 互換順序或者將 fullTimesalary 互換順序,都是在定義一個不同的結構體型別

結構體指標

初始化結構體的時候,可以宣告一個指向結構體的指標:

ross_pointer := &Empolyee{
	firstName:"ross",
	lastName:"Bingo",
	salary:1000,
	fullTime:true,
}
複製程式碼

上面的程式碼,建立了一個指向 Empolyee 結構體的指標 ross_pointer。可以通過指標訪問結構體的成員:

fmt.Println(*ross_pointer)
fmt.Println("firstName:",(*ross_pointer).firstName)
fmt.Println("firstName:",ross_pointer.lastName)
複製程式碼

輸出:

{ross Bingo 1000 true}
firstName: ross
firstName: Bingo
複製程式碼

ross_pointer 是一個結構體變數,所以 (*ross_pointer).firstNameross_pointer.lastName 都是正確的訪問方式 。

匿名成員

定義結構體時可以只指定成員型別,不用指定成員名,Go 會自動地將成員型別作為成員名。這種結構體成員稱為匿名成員。這個結構體成員的型別必須是命名型別或者是指向命名型別的指標。

type Week struct{
	string
	int
	bool
}
func main() {
	week := Week{"Friday",1000,true}
	fmt.Println(week)
}
複製程式碼

上面的程式碼定義了結構體 Week ,有 stringintbool 三個成員變數,變數名與型別相同。 這種定義方式可以和指定成員名混合使用,例如:

type Empolyee struct{
	firstName,lastName string
	salary int
	bool
}
複製程式碼

結構體巢狀

Go 有結構體巢狀機制,一個結構體可以作為另一個結構體型別的成員。

type Salary struct {
	basic int
	workovertime int
}

type Empolyee struct{
	firstName,lastName string
	salary Salary
	bool
}

func main() {
	ross := Empolyee{
	    firstName:"Ross",
            lastName:"Bingo",
            bool:true,
            salary:Salary{1000,100},
	}
	fmt.Println(ross.salary.basic);
}
複製程式碼

我們新定義了結構體型別 Salary,將 Empolyee 成員型別修改成結構體型別 Salary。 建立了結構體 ross,想要訪問成員 salary 裡面的成員還是可以採用 . 的方式,例如:ross.salary.basic
如果結構體巢狀層數過多時,想要訪問最裡面結構體成員時,採用上面這種訪問方式就會牽扯很多中間變數,造成程式碼很臃腫。可以採用上面的匿名成員方式簡化這種操作。
採用匿名成員方式重新定義結構體型別 Empolyee

type Empolyee struct{
	firstName,lastName string
	Salary    // 匿名成員
	bool
}
func main() {
	ross := Empolyee{
		firstName:"Ross",
		lastName:"Bingo",
		bool:true,
		Salary:Salary{1000,100},
	}
	fmt.Println(ross.basic);         // 訪問方式一
	fmt.Println(ross.Salary.basic);  // 訪問方式二
	ross.basic = 1200
	fmt.Println(ross.basic)          // update
}
複製程式碼

上面兩種方式是等價的。通過這種方式,簡化了訪問過程。
需要注意的是,被巢狀的匿名結構體成員中,不能與上一層結構體成員同名。

type Empolyee struct{
	firstName,lastName string
	Salary
	basic int
	bool
}
func main() {
	ross := Empolyee{
		firstName:"Ross",
		lastName:"Bingo",
		bool:true,
		Salary{1000,100},
	}
	fmt.Println(ross.basic)
}
複製程式碼

上面的程式碼,我們修改了結構體 Empolyee ,多新增了一個與 Salary.basic 同名的成員,但是編譯出錯: mixture of field:value and value initializers

可匯出的成員

一個 Go 包中的變數、函式首字母大寫,那這個變數或函式是可以匯出的。這是 Go 最主要的訪問控制機制。如果一個結構體的成員變數名首字母大寫,那這個成員也是可匯出的。一個結構體可以同時包含可匯出和不可匯出的成員變數。
在路徑 WORKSPACE/src/org/employee.go 建立一個名為 org 的包,新增如下程式碼:

// employee.go
package org
type Employee struct {
	FirstName,LastName string
	salary int
	fullTime bool
}
複製程式碼

上面的 Employee 結構體,只有變數 FirstNameLastName 是可匯出的。當然,Employee 也是可匯出的。 在 main 包中匯入 org 包:

// main.go
package main
import (
	"org"
	"fmt"
)
func main() {
	ross := org.Employee{
		FirstName:"Ross",
		LastName:"Bingo",
		salary:1000,     
	}
	fmt.Println(ross)
}
複製程式碼

上面的程式碼編譯出錯,因為成員變數 salary 是不可匯出的: unknown field 'salary' in struct literal of type org.Employee

因為 Employee 來自包 org,所以用 org.Employee 去建立結構體 ross。可以採用型別別名簡化:

package main

import (
	"org"
	"fmt"
)

type Employee org.Employee; 

func main() {
	ross := Employee{
		FirstName:"Ross",
		LastName:"Bingo",
	}
	fmt.Println(ross)
}
複製程式碼

輸出:

{Ross Bingo 0 false}
複製程式碼

結構體比較

如果結構體的所有成員都是可比較的,則這個結構體就是可比較的。可以使用 ==!= 作比較,其中 == 是按照順序比較兩個結構體變數的成員變數。

type Employee struct {
	FirstName,LastName string
	salary int
	fullTime bool
}
func main() {
	ross := Employee{
		FirstName:"Ross",
		LastName:"Bingo",
	}
	jack := Employee{
		FirstName:"Jack",
		LastName:"Lee",
	}
	fmt.Println(ross == jack)
}
複製程式碼

輸出:

false
複製程式碼

不同型別的結構體變數是不能比較的:

type User struct {
	username string
}
type Employee struct {
	FirstName,LastName string
	salary int
	fullTime bool
}
func main() {
	ross := Employee{
		FirstName:"Ross",
		LastName:"Bingo",
	}
	user := User{
		username:"Seekload",
	}
	fmt.Println(ross == user)
}
複製程式碼

編譯出錯: invalid operation: ross == user (mismatched types Employee and User) .
然而,如果有成員是不能比較的,例如:map,則這個結構體是不能比較的。


(全文完)

原創文章,若需轉載請註明出處!
歡迎掃碼關注公眾號「Golang來啦」或者移步 seekload.net ,檢視更多精彩文章。

公眾號「Golang來啦」給你準備了一份神祕學習大禮包,後臺回覆【電子書】領取!

公眾號二維碼

相關文章