- 原文地址:Part 16: Structures
- 原文做者:Naveen R
- 譯者:咔嘰咔嘰 轉載請註明出處。
結構是用戶定義的類型,表示字段集合。它能夠將一組數據放到結構中,而不是將它們做爲單獨的類型進行維護。golang
例如,員工具備firstName
,lastName
和age
。將這三個屬性組合成一個結構employee
是有意義的。app
type Employee struct {
firstName string
lastName string
age int
}
複製代碼
上面的代碼片斷聲明瞭一個結構類型Employee
,其中包含firstName
,lastName
和age
字段。經過在單行中聲明屬於同一類型的字段,後跟類型名稱,可使該結構更緊湊。在上面的結構體中,firstName
和lastName
屬於同一個類型的字符串,所以能夠將struct
重寫爲工具
type Employee struct {
firstName, lastName string
age, salary int
}
複製代碼
上面的Employee
結構稱爲命名結構,由於它建立了一個名爲Employee
的新類型。spa
也能夠在不聲明新類型的狀況下聲明結構,而且這些類型的結構稱爲匿名結構。指針
var employee struct {
firstName, lastName string
age int
}
複製代碼
上面的代碼片斷建立了一個匿名結構employee
。code
讓咱們使用一個簡單的程序定義一個命名結構Employee
。ci
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
//creating structure using field names
emp1 := Employee{
firstName: "Sam",
age: 25,
salary: 500,
lastName: "Anderson",
}
//creating structure without using field names
emp2 := Employee{"Thomas", "Paul", 29, 800}
fmt.Println("Employee 1", emp1)
fmt.Println("Employee 2", emp2)
}
複製代碼
在上面程序的第 7 行中,咱們建立了一個叫Employee
的結構體。在第 15 行,初始化了一個emp1
結構。字段名稱的順序沒必要與聲明結構類型時的順序相同。這裏咱們更改了lastName
的位置並將其移到了最後。這將沒有任何問題。get
在第 23 行,經過省略字段名來初始化emp2
。在這種狀況下,必須保持字段的順序與結構聲明中指定的相同。 輸出,編譯器
Employee 1 {Sam Anderson 25 500}
Employee 2 {Thomas Paul 29 800}
複製代碼
package main
import (
"fmt"
)
func main() {
emp3 := struct {
firstName, lastName string
age, salary int
}{
firstName: "Andreah",
lastName: "Nikola",
age: 31,
salary: 5000,
}
fmt.Println("Employee 3", emp3)
}
複製代碼
在上述程序的第 8 行,定義了匿名結構變量emp3
。正如咱們已經提到的,這被稱爲匿名結構,由於它只建立一個新的struct
變量emp3
,並無定義任何新的結構類型。 該程序輸出,
Employee 3 {Andreah Nikola 31 5000}
複製代碼
定義結構而且未使用任何值顯式初始化它時,默認狀況下會爲結構的字段分配其零值。
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
var emp4 Employee //zero valued structure
fmt.Println("Employee 4", emp4)
}
複製代碼
上面的程序定義了emp4
,但它沒有用任何值初始化。所以,firstName
和lastName
被賦予字符串的零值,即"",而age
,salary
被賦予int
的零值,即 0。此程序輸出\
Employee 4 { 0 0}
複製代碼
也能夠爲某些字段指定值而忽略其他字段。在這種狀況下,忽略的字段將被賦予零值。
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
emp5 := Employee{
firstName: "John",
lastName: "Paul",
}
fmt.Println("Employee 5", emp5)
}
複製代碼
Run in playgroud 在上面的第 14 和 15 行,firstName
和lastName
被初始化,而age
和salary
則沒有。所以,age
和salary
被賦予 0。該程序輸出,
Employee 5 {John Paul 0 0}
複製代碼
.
操做符用來訪問結構的字段
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
emp6 := Employee{"Sam", "Anderson", 55, 6000}
fmt.Println("First Name:", emp6.firstName)
fmt.Println("Last Name:", emp6.lastName)
fmt.Println("Age:", emp6.age)
fmt.Printf("Salary: $%d", emp6.salary)
}
複製代碼
上面程序中的emp6.firstName
訪問emp6
結構的firstName
字段。該程序輸出,
First Name: Sam
Last Name: Anderson
Age: 55
Salary: $6000
複製代碼
也能夠建立零結構,而後稍後爲其字段分配值。
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
var emp7 Employee
emp7.firstName = "Jack"
emp7.lastName = "Adams"
fmt.Println("Employee 7:", emp7)
}
複製代碼
在上面的程序中,定義了emp7
,而後爲firstName
和lastName
賦值。該程序輸出,
Employee 7: {Jack Adams 0 0}
複製代碼
能夠建立一個指向結構的指針。
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
emp8 := &Employee{"Sam", "Anderson", 55, 6000}
fmt.Println("First Name:", (*emp8).firstName)
fmt.Println("Age:", (*emp8).age)
}
複製代碼
上面程序中的emp8
是指向Employee
結構的指針。(* emp8).firstName
是訪問firstName
字段的語法。該程序輸出,
First Name: Sam
Age: 55
複製代碼
Go 語言可使用emp8.firstName
來訪問firstName
字段,從而能夠不用經過顯式解除引用(* emp8).firstName
來訪問。
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
emp8 := &Employee{"Sam", "Anderson", 55, 6000}
fmt.Println("First Name:", emp8.firstName)
fmt.Println("Age:", emp8.age)
}
複製代碼
咱們使用emp8.firstName
來訪問上面程序中的firstName
字段,這個程序一樣輸出,
First Name: Sam
Age: 55
複製代碼
可使用包含不帶字段名稱去建立結構。這些字段稱爲匿名字段。
下面的代碼片斷建立了一個結構Person
,它有兩個匿名字段string
和int
type Person struct {
string
int
}
複製代碼
讓咱們使用匿名字段寫一段代碼,
package main
import (
"fmt"
)
type Person struct {
string
int
}
func main() {
p := Person{"Naveen", 50}
fmt.Println(p)
}
複製代碼
在上面的程序中,Person
是一個帶有兩個匿名字段的結構。 p := Person {「Naveen」,50}
定義了Person
的變量。該程序輸出{Naveen 50}
。
匿名字段沒有名稱,默認狀況下,匿名字段的名稱也是其類型的名稱。例如,在上面的Person
結構的狀況下,雖然字段是匿名的,但默認狀況下它們採用字段類型的名稱。因此Person
結構有 2 個字段,名稱爲string
和int
。
package main
import (
"fmt"
)
type Person struct {
string
int
}
func main() {
var p1 Person
p1.string = "naveen"
p1.int = 50
fmt.Println(p1)
}
複製代碼
在上面程序的第 14 和 15 行中,咱們使用它們的類型string
和int
做爲字段名稱來訪問Person
結構的匿名字段。上述程序的輸出是,
{naveen 50}
複製代碼
若是結構包含一個struct
類型的字段那稱該結構爲嵌套結構。
package main
import (
"fmt"
)
type Address struct {
city, state string
}
type Person struct {
name string
age int
address Address
}
func main() {
var p Person
p.name = "Naveen"
p.age = 50
p.address = Address {
city: "Chicago",
state: "Illinois",
}
fmt.Println("Name:", p.name)
fmt.Println("Age:",p.age)
fmt.Println("City:",p.address.city)
fmt.Println("State:",p.address.state)
}
複製代碼
上述程序中的Person
結構有一個字段address
,而該字段又是一個結構。將輸出,
Name: Naveen
Age: 50
City: Chicago
State: Illinois
複製代碼
一個結構中的匿名結構的字段稱爲提高字段,由於它們能夠被訪問就像它們屬於原結構同樣。這個定義有點複雜,因此讓咱們直接用
type Address struct {
city, state string
}
type Person struct {
name string
age int
Address
}
複製代碼
在上面的代碼片斷中,Person
結構有一個匿名字段Address
,它是一個結構。如今,Address
結構的字段即city
和state
被稱爲提高字段,由於它們能夠和Person
結構聲明的字段同樣被訪問。
package main
import (
"fmt"
)
type Address struct {
city, state string
}
type Person struct {
name string
age int
Address
}
func main() {
var p Person
p.name = "Naveen"
p.age = 50
p.Address = Address{
city: "Chicago",
state: "Illinois",
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.city) //city is promoted field
fmt.Println("State:", p.state) //state is promoted field
}
複製代碼
在上述程序的第 26 和 27 行中,p
訪問提高字段city
和state
,就好像它們是在結構p
自己中被聲明瞭,而後使用語法p.city
和p.state
訪問。該程序輸出,
Name: Naveen
Age: 50
City: Chicago
State: Illinois
複製代碼
若是結構類型以大寫字母開頭,則它是導出類型,能夠從其餘包訪問它。相似地,若是結構的字段以大寫字母開頭,則也能夠從其餘包中訪問它們。
讓咱們編寫一個包含自定義包的程序,以便更好地理解這一點。
在 go 工做區的 src 目錄中建立一個名爲 structs 的文件夾。在 structs 內建立另外一個目錄 computer。
進入 computer 目錄,用 spec.go 爲名稱保存該代碼。
package computer
type Spec struct { //exported struct
Maker string //exported field
model string //unexported field
Price int //exported field
}
複製代碼
上面的代碼片斷建立了一個包computer
,其中包含一個導出的結構類型Spec
,它包含兩個導出字段Maker
和Price
以及一個非導出的字段model
。讓咱們從main package
導入這個包並使用Spec
結構。
在 structs 目錄中建立一個名爲 main.go 的文件,並在 main.go 中編寫如下程序
package main
import "structs/computer"
import "fmt"
func main() {
var spec computer.Spec
spec.Maker = "apple"
spec.Price = 50000
fmt.Println("Spec:", spec)
}
複製代碼
包結構應該以下所示,
src
structs
computer
spec.go
main.go
複製代碼
在上面程序的第 3 行中,咱們導入了 computer 包。在第 8 和 9 行中,咱們訪問了Spec
結構的兩個導出字段Maker
和Price Spec
的價格。能夠經過執行命令go install structs
,而後再執行workspacepath/bin/structs
來運行該程序
該程序將輸出,Spec: {apple 50000}
若是咱們嘗試訪問非導出的字段model
,編譯器會報錯。咱們用如下代碼替換上述 main.go 的內容。
package main
import "structs/computer"
import "fmt"
func main() {
var spec computer.Spec
spec.Maker = "apple"
spec.Price = 50000
spec.model = "Mac Mini"
fmt.Println("Spec:", spec)
}
複製代碼
在上述程序的第 10 行中,咱們嘗試訪問非導出的字段model
。運行此程序將致使編譯錯誤spec.model undefined (cannot refer to unexported field or method model)
結構是值類型,若是它們的每一個字段都是可比較的,則可比較。兩個結構變量的相應字段相等,則認爲它們是相等的。
package main
import (
"fmt"
)
type name struct {
firstName string
lastName string
}
func main() {
name1 := name{"Steve", "Jobs"}
name2 := name{"Steve", "Jobs"}
if name1 == name2 {
fmt.Println("name1 and name2 are equal")
} else {
fmt.Println("name1 and name2 are not equal")
}
name3 := name{firstName:"Steve", lastName:"Jobs"}
name4 := name{}
name4.firstName = "Steve"
if name3 == name4 {
fmt.Println("name3 and name4 are equal")
} else {
fmt.Println("name3 and name4 are not equal")
}
}
複製代碼
Run in playgroud 在上面的程序中,name
結構包含兩個字符串字段。因爲字符串具備可比性,所以能夠經過name
結構的兩個變量去比較。
在上面的程序中,name1 和 name2 相等,而 name3 和 name4 則不相等。該程序將輸出,
name1 and name2 are equal
name3 and name4 are not equal
複製代碼
若是結構變量包含沒法比較的字段,則該結構不具備可比性。
package main
import (
"fmt"
)
type image struct {
data map[int]int
}
func main() {
image1 := image{data: map[int]int{
0: 155,
}}
image2 := image{data: map[int]int{
0: 155,
}}
if image1 == image2 {
fmt.Println("image1 and image2 are equal")
}
}
複製代碼
在上面的程序中,image
結構類型包含一個map
類型的字段data
。因爲map
不具備可比性,所以沒法比較image1
和image2
。若是運行此程序,編譯將失敗,錯誤爲main.go:18: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared).
。