共计 7713 个字符,预计需要花费 20 分钟才能阅读完成。
本文已收录如何疾速学习 Go 的 struct 数据类型。涵盖 PHP、JavaScript、Linux、Golang、MySQL、Redis 和开源工具等等相干内容。
什么是构造体
构造是示意字段汇合的用户定义类型。它能够用于将数据分组为单个单元而不是将每个数据作为独自的值的中央。
例如,员工有 firstName、lastName 和 age。将这三个属性分组到一个名为Employee
。
type Employee struct {
firstName string
lastName string
age int
}
下面的代码段申明了一个构造类型 Employee,其中蕴含字段 firstName、lastName 和 age。下面的 Employee 构造称为命名构造,因为它创立了一个名为 Employme 的新数据类型,能够应用该数据类型创立 Employ 构造。
通过在一行中申明属于同一类型的字段,而后在类型名称前面加上该字段,也能够使该构造更加紧凑。在下面的 struct 中,firstName 和 lastName 属于同一类型字符串,因而该 struct 能够重写为:
type Employee struct {
firstName, lastName string
age int
}
只管下面的语法节俭了几行代码,但它并没有使字段声显著式。请防止应用上述语法。
创立构造体
让咱们应用以下简略程序申明一个命名的 structEmployee。
package main
import ("fmt")
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
//creating struct specifying field names
emp1 := Employee{
firstName: "Sam",
age: 25,
salary: 500,
lastName: "Anderson",
}
//creating struct without specifying field names
emp2 := Employee{"Thomas", "Paul", 29, 800}
fmt.Println("Employee 1", emp1)
fmt.Println("Employee 2", emp2)
}
在上述程序的第 7 行中,咱们创立了一个命名的构造类型 Employee。在上述程序的第 17 行中,emp1 构造是通过为每个字段名指定值来定义的。申明构造类型时,字段的程序不用与字段名的程序雷同。在这种状况下。咱们已更改 lastName 的地位并将其移到开端。这将不会有任何问题。
在上述程序的第 25 行中,通过省略字段名来定义 emp2。在这种状况下,必须放弃字段的程序与构造申明中指定的程序雷同。请防止应用此语法,因为它会使您难以确定哪个字段的值。咱们在此处指定此格局只是为了了解这也是一个无效语法:)
以上程序打印为:
Employee 1 {Sam Anderson 25 500}
Employee 2 {Thomas Paul 29 800}
创立匿名构造体
能够在不创立新数据类型的状况下申明构造。这些类型的构造称为匿名构造。
package main
import ("fmt")
func main() {
emp3 := struct {
firstName string
lastName string
age int
salary int
}{
firstName: "Andreah",
lastName: "Nikola",
age: 31,
salary: 5000,
}
fmt.Println("Employee 3", emp3)
}
在上述程序的第 8 行中,定义了一个匿名构造变量 emp3。正如咱们曾经提到的,这个构造称为 anonymous,因为它只创立一个新的构造变量 emp3,而没有定义任何新的构造类型,如命名构造。
上述代码打印的后果为:
Employee 3 {Andreah Nikola 31 5000}
拜访构造体字段
运算符 .
用于拜访构造的各个字段。
package main
import ("fmt")
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp6 := Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", emp6.firstName)
fmt.Println("Last Name:", emp6.lastName)
fmt.Println("Age:", emp6.age)
fmt.Printf("Salary: $%d\n", emp6.salary)
emp6.salary = 6500
fmt.Printf("New Salary: $%d", emp6.salary)
}
下面程序中的 emp6.firstName 拜访 emp6 构造的 firstName 字段。在第 25 行中,咱们批改了员工的工资。此程序打印。
First Name: Sam
Last Name: Anderson
Age: 55
Salary: $6000
New Salary: $6500
构造体零值
当定义了一个构造并且没有用任何值显式初始化它时,默认状况下会为该构造的字段调配零值。
package main
import ("fmt")
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
var emp4 Employee //zero valued struct
fmt.Println("First Name:", emp4.firstName)
fmt.Println("Last Name:", emp4.lastName)
fmt.Println("Age:", emp4.age)
fmt.Println("Salary:", emp4.salary)
}
下面的程序定义了 emp4,但没有用任何值初始化。因而,firstName 和 lastName 被指定为字符串的零值,字符串为空字符串“”,age 和 salary 被指定为零值 int,即 0。此程序打印
First Name:
Last Name:
Age: 0
Salary: 0
也能够为某些字段指定值,而疏忽其余字段。在这种状况下,被疏忽的字段被赋值为零。
package main
import ("fmt")
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp5 := Employee{
firstName: "John",
lastName: "Paul",
}
fmt.Println("First Name:", emp5.firstName)
fmt.Println("Last Name:", emp5.lastName)
fmt.Println("Age:", emp5.age)
fmt.Println("Salary:", emp5.salary)
}
在下面的程序中。第 16 号和第 17 号,firstName 和 lastName 被初始化,而年龄和薪水没有初始化。因而,年龄和工资被指定为零值。此程序输入:
First Name: John
Last Name: Paul
Age: 0
Salary: 0
构造体指针
也能够创立指向构造的指针。
package main
import ("fmt")
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp8 := &Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", (*emp8).firstName)
fmt.Println("Age:", (*emp8).age)
}
下面程序中的 emp8 是指向 Employee 构造的指针。(*emp8)。firstName 是拜访 emp8 构造的 firstName 字段的语法。此程序打印:
First Name: Sam
Age: 55
Go 语言为咱们提供了应用 emp8.firstName 而不是显式勾销援用(*emp8)的选项。firstName 以拜访 firstName 字段。
package main
import ("fmt")
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp8 := &Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", emp8.firstName)
fmt.Println("Age:", emp8.age)
}
咱们曾经应用 emp8.firstName 拜访上述程序中的 firstName 字段,该程序还输入:
First Name: Sam
Age: 55
匿名字段
能够应用只蕴含类型而不蕴含字段名的字段创立构造。这类字段称为匿名字段。上面的代码段创立了一个 struct Person,它有两个匿名字段 string 和 int:
type Person struct {
string
int
}
即便匿名字段没有显式名称,默认状况下,匿名字段的名称是其类型的名称。例如,在下面的 Person 构造中,尽管字段是匿名的,但默认状况下它们采纳字段类型的名称。所以 Person 构造有两个字段,别离是名称字符串和 int。
package main
import ("fmt")
type Person struct {
string
int
}
func main() {
p1 := Person{
string: "naveen",
int: 50,
}
fmt.Println(p1.string)
fmt.Println(p1.int)
}
在上述程序的第 17 行和第 18 行中,咱们拜访 Person 构造的匿名字段,应用它们的类型作为字段名,别离是 string 和 int。上述程序的输入为:
naveen
50
构造体嵌套
构造可能蕴含字段,而字段又是构造。这些类型的构造称为嵌套构造。
package main
import ("fmt")
type Address struct {
city string
state string
}
type Person struct {
name string
age int
address Address
}
func main() {
p := Person{
name: "Naveen",
age: 50,
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 构造具备字段地址,而字段地址又是一个构造。此程序打印:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
字段降级
属于构造中匿名构造字段的字段称为晋升字段,因为能够像拜访蕴含匿名构造字段构造一样拜访它们。我能够了解这个定义相当简单,所以让咱们深入研究一些代码来了解它。
type Address struct {
city string
state string
}
type Person struct {
name string
age int
Address
}
在下面的代码段中,Person 构造有一个匿名字段 Address,它是一个构造。当初,Address 的字段,即 city 和 state,被称为 promoted 字段,因为能够像间接在 Person 构造自身中申明一样拜访它们。
package main
import ("fmt")
type Address struct {
city string
state string
}
type Person struct {
name string
age int
Address
}
func main() {
p := Person{
name: "Naveen",
age: 50,
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
}
在下面程序的第 29 行和第 30 行中,能够拜访晋升字段 city 和 state,就如同它们是应用语法 p.city 和 p.state 在构造 p 中申明的一样。此程序打印:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
构造体导出
如果构造类型以大写字母结尾,则它是导出类型,能够从其余包拜访。相似地,如果构造的字段以 caps 结尾,则能够从其余包拜访它们。让咱们编写一个具备自定义包的程序来更好地了解这一点。在 Documents 目录中创立名为 structs 的文件夹。请随便在任何您喜爱的中央创立它。我更喜爱我的文档目录。
mkdir ~/Documents/structs
创立一个 gomod,并命名为structs
。
cd ~/Documents/structs/
go mod init structs
创立另外一个目录 computer
申明一个构造体。
mkdir computer
创立一个 spec.go
文件,并写入如下内容:
package computer
type Spec struct { //exported struct
Maker string //exported field
Price int //exported field
model string //unexported field
}
下面的代码片段创立了一个程序包计算机,其中蕴含一个导出的构造类型 Spec,其中有两个导出的字段 Maker 和 Price,以及一个未导出的字段模型。让咱们从主包导入这个包并应用 Spec 构造。
创立名为 main 的文件。进入 structs 目录并在 main.go 中编写以下程序:
package main
import (
"structs/computer"
"fmt"
)
func main() {
spec := computer.Spec {
Maker: "apple",
Price: 50000,
}
fmt.Println("Maker:", spec.Maker)
fmt.Println("Price:", spec.Price)
}
这个构造体如下构造体:
├── structs
│ ├── computer
│ │ └── spec.go
│ ├── go.mod
│ └── main.go
在下面程序的第 4 行,咱们导入计算机包。在第 13 行和第 14 行,咱们拜访 struct Spec 的两个导出字段 Maker 和 Price。这个程序能够通过执行命令 go-install,而后执行 structs 命令来运行。
go install
structs
运行之后如下后果:
Maker: apple
Price: 50000
如果咱们试图拜访未报告的字段模型,编译器会埋怨。更换 main 的内容。应用以下代码。
package main
import (
"structs/computer"
"fmt"
)
func main() {
spec := computer.Spec {
Maker: "apple",
Price: 50000,
model: "Mac Mini",
}
fmt.Println("Maker:", spec.Maker)
fmt.Println("Price:", spec.Price)
}
在上述程序的第 12 行中,咱们尝试拜访未报告的字段模型。运行此程序将导致编译谬误。
# structs
./main.go:12:13: unknown field 'model' in struct literal of type computer.Spec
因为模型字段未报告,因而无奈从其余包拜访它。
构造体比拟
构造是值类型,如果它们的每个字段都是可比拟的,则能够进行比拟。如果两个构造变量的对应字段相等,则认为它们相等。
package main
import ("fmt")
type name struct {
firstName string
lastName string
}
func main() {
name1 := name{
firstName: "Steve",
lastName: "Jobs",
}
name2 := name{
firstName: "Steve",
lastName: "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{firstName: "Steve",}
if name3 == name4 {fmt.Println("name3 and name4 are equal")
} else {fmt.Println("name3 and name4 are not equal")
}
}
在下面的程序中,名称构造类型蕴含两个字符串字段。因为字符串是可比拟的,因而能够比拟类型名的两个构造变量。在下面的程序中,name1 和 name2 相等,而 name3 和 name4 不相等。此程序将输入:
name1 and name2 are equal
name3 and name4 are not equal
如果构造变量蕴含不可比拟的字段,那么它们就不可比拟(感激 reddit 的 alasija 指出这一点)。
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")
}
}
在下面的程序中,图像构造类型蕴含类型映射的字段数据。地图是不可比拟的,因而无奈比拟 image1 和 image2。如果运行此程序,编译将失败并返回谬误。
./prog.go:20:12: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared)