[Golang] Struct
此篇為各筆記之整理,非原創內容,資料來源可見下方連結與文後參考資料: 👍 Structures in Go (structs) @ medium > rungo
TL;DR
// create struct on the fly
foo := struct {
Hello string
}{
Hello: "World",
}
Anonymous Field:
type User struct {
firstName string
lastName string
birthDate string
createdAt time.Time
}
type Admin struct {
email string
password string
// anonymous / embedded field:直接帶入 User 裡面有的欄位
User
}
func main() {
a := Admin{}
a.firstName = "Aaron"
a.lastName = "Chen"
// 使用 anonymous/embedded field 可以直接取得該 nested struct 的值
// 這個行為稱作 Promoted
fmt.Println(a.firstName, a.lastName)
}
三種宣告 Person struct 的方式:
- 使用 new syntax:第二種和第三種寫法是一樣的
var user1 *Person // nil
user2 := &Person{} // {},user2.firstName 會是 ""
user3 := new(Person) // {},user3.firstName 會是 ""
structs 是在 GO 中的一種資料型態,它就類似 JavaScript 中的物件(Object)或是 Ruby 中的 Hash。
定義與使用基本的 struct
建立一個 person 型別,它本質上是 struct:
// STEP 1:建立一個 person 型別,它本質上是 struct
type Person struct {
firstName string
lastName string
}
// 等同於
type Person struct {
firstName, lastName string
}
有幾種不同的方式可以根據 struct 來建立變數的:
func main() {
// 方法一:根據資料輸入的順序決定誰是 firstName 和 lastName
alex := Person{"Alex", "Anderson"}
// 直接取得 struct 的 pointer
alex := &Person{"Alex", "Anderson"}
// 方法二(建議)
alex := Person{
firstName: "Alex",
lastName: "Anderson",
}
// 方法三:先宣告再賦值
var alex Person
alex.firstName = "Alex"
alex.lastName = "Anderson"
// 方法四
var alex = Person{
firstName: "Alex",
lastName: "Anderson",
}
}
輸出建立好的 struct:
// 如果希望一併輸出 field name 的話
fmt.Printf("%+v", alex) // {firstName:Alex lastName:Anderson}
fmt.Println(alex) // {Alex Anderson}
取值的方式:
alex.firstName // 不能用 alex["firstName"],因為 alex 是 struct,不是 map
alex.lastName
定義匿名的 struct(anonymous struct)
也可以不先宣告 struct 直接建立個 struct:
foo := struct {
Hello string
}{
Hello: "World",
}
當 pointer 指稱到的是 struct 時
當 pointer 指稱到的是 struct 時,可以直接使用這個 pointer 來對該 struct 進行設值和取值。在 golang 中可以直接使用 pointer 來修改 struct 中的欄 位。一般來說,若想要透過 struct pointer(&v
)來修改該 struct 中的屬性,需要先解出其值(*p
)後使用 (*p).X = 10
,但這樣做太麻煩了,因此在 golang 中允許開發者直接使用 p.X
的方式來修改:
type Person struct {
name string
age int32
}
func main() {
p := &Person{
name: "Aaron",
}
// golang 中允許開發者直接使用 `p.age` 的方式來設值與取值
p.age = 10 // 原本應該要寫 (*p).X = 10
fmt.Printf("%+v", p) // {name:Aaron age:10}
}
另外,使用 struct pointer 時才可以修改到原本的物件,否則會複製一個新的:
func main() {
r1 := rectangle{"Green"}
// 複製新的,指稱到不同位置
r2 := r1
r2.color = "Pink"
fmt.Println(r2) // Pink
fmt.Println(r1) // Green
// 指稱到相同位置
r3 := &r1
r3.color = "Red"
fmt.Println(r3) // Red
fmt.Println(r1) // Red
}
在 struct 內關聯另一個 struct(nested struct)
在一個 struct 內可以包含另一個 struct:
// STEP 1:定義外層 struct
type person struct {
firstName string
lastName string
contact contactInfo
}
// STEP 2:定義內層 struct
type contactInfo struct {
email string
zipCode int
}
func main() {
// STEP 3:建立變數
jim := person{
firstName: "Jim",
lastName: "Party",
contact: contactInfo{
email: "jim@gmail.com",
zipCode: 94000,
},
}
alex := person{
firstName: "Alex",
lastName: "Anderson",
}
// STEP 4:印出變數
fmt.Printf("%+v\n", jim) // {firstName:Jim lastName:Party contact:{email:jim@gmail.com zipCode:94000}}
fmt.Println(jim) // {Jim Party {jim@gmail.com 94000}}
fmt.Printf("%+v\n", alex) // {firstName:Alex lastName:Anderson contact:{email: zipCode:0}}
fmt.Println(alex) // {Alex Anderson { 0}}
}
Struct field Tag(meta-data)
struct field tag 會在 struct 的 value 後面使用 backtick 來表示,例如 json:"name"
:
type User struct {
Name string `json:"name"`
Password string `json:"-"`
PreferredFish []string `json:"preferredFish,omitempty"`
CreatedAt time.Time `json:"createdAt"`
}
在 field tag 中還能帶入其他關鍵字,舉例來說:
omitempty
:指的是該欄位沒值的話,就不要顯示欄位名稱-
:表示忽略掉該欄位,Marshal 時該欄位不會出現在 JSON 中,Unmarshal 時該欄位也不會被處理
Anonymous fields
在 struct 中不一定要替欄位建立名稱,而是可以直接使用 data types,而 Go 會使用這個 data type 當作欄位名稱:
type AnonymousField struct {
string // 相似於 string string
bool // 相似於 bool bool
int // 相似於 int int
}
func main() {
anonymousField := AnonymousField{
"person", true, 30,
}
fmt.Printf("%+v", anonymousField) // {string:person bool:true int:30}
}
Function Fields
struct 中的 field 也可以是 function
type GetDisplayNameType func(string, string) string
type Person struct {
FirstName, LastName string
GetDisplayName GetDisplayNameType
}
func main() {
p := Person{
FirstName: "Aaron",
LastName: "Chen",
GetDisplayName: func(firstName, lastName string) string {
return firstName + " " + lastName
},
}
displayName := p.GetDisplayName(p.FirstName, p.LastName)
fmt.Println(displayName) // Aaron Chen
}
Promoted fields / Anonymous / Embedded
- Promoted fields and methods in Go @ medium > golangspec
- Structures in Go (structs) @ medium > rungo
定義 Promoted fields 的 struct
在 Golang 中 struct 的 fields name 可以省略,沒有 field name 的 name 被稱作 anonymous 或 embedded。在這種情況下,會直接使用 「Type 的名稱」來當作 field name:
// https://medium.com/golangspec/promoted-fields-and-methods-in-go-4e8d7aefb3e3
type Person struct {
name string
age int32
}
func (p Person) IsAdult() bool {
return p.age >= 18
}
type Employee struct {
position string
}
func (e Employee) IsManager() bool {
return e.position == "manager"
}
type Record struct {
Person
Employee
}
func main() {
fmt.Printf("%+v", record)
}
如果 nested anonymous struct 中的欄位和其 parent struct 的欄位名稱有衝突時,則該欄位不會被 promoted。