golang结构

分类: glolang 发布时间: 2018-09-12 04:27:50
什么是结构

结构是用户定义的类型,表示字段集合。它可以在有意义的地方使用,将数据分组到一个单元中,而不是将它们作为单独的类型进行维护。

例如,员工具有firstName,lastName和age。将这三个属性组合成一个结构是有意义的employee。

申明一个结构
type Employee struct {  
    firstName string
    lastName  string
    age       int
}

上面的代码片段声明了一个结构类型Employee,其中包含firstName,lastName和age字段。通过在单行中声明属于同一类型的字段,后跟类型名称,也可以使该结构更紧凑。另外,在上述结构firstName和lastName属于相同的类型string,并因此该结构可以被改写为

type Employee struct {  
    firstName, lastName string
    age, salary         int
}

上面的Employee结构称为命名结构,因为它创建了一个名为的新类型Employee,可用于创建类型的结构Employee。

可以在不声明新类型的情况下声明结构,并且这些类型的结构称为匿名结构。

var employee struct {  
        firstName, lastName string
        age int
}

上面的代码片段创建了一个匿名结构 employee。

创建命名结构

让我们使用一个简单的程序定义一个命名结构Employee。

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并将其移至最后。这将没有任何问题。

在上述程序的第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,并且不定义任何新的struct类型。

该程序输出,

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)
}

在上面的程序中。没有14和15,firstName和lastName被初始化,而age并salary没有。因此age,salary并分配他们的零值。该程序输出,

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访问结构的firstName字段emp6。该程序输出,

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是指向Employeestruct 的指针。(*emp8).firstName是访问结构firstName字段的语法emp8。该程序输出,

First Name: Sam  
Age: 55

该语言为我们提供了使用emp8.firstName而不是显式取消引用(*emp8).firstName来访问该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结构的情况下,虽然字段是匿名的,但默认情况下它们采用字段类型的名称。所以Personstruct有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中,我们使用它们的类型作为字段名称来访问Person结构的匿名字段,字段名称分别是“string”和“int”。上述程序的输出是,

{naveen 50}
嵌套结构

struct可能包含一个字段,而该字段又是一个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
}

在上面的代码片段中,Personstruct有一个匿名字段Address,它是一个struct。现在的场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中,提升的字段city和state访问就好像它们是p使用语法p.city和结构本身声明的一样p.state。该程序输出,

Name: Naveen  
Age: 50  
City: Chicago  
State: Illinois
导出的结构和字段

如果结构类型以大写字母开头,那么它是导出类型,可以从其他包访问它。类似地,如果结构的字段以大写字母开头,则可以从其他包中访问它们。

让我们编写一个包含自定义包的程序,以便更好地理解这一点。

在go工作区structs的src目录中创建一个名为的文件夹。创建另一个目录computer中structs。

在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。让我们从主包导入这个包并使用Spec结构。

main.go在structs目录中创建一个名为的文件,并在里面编写以下程序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中,我们访问两个导出的字段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(不能引用未导出的字段或方法模型)

结构平等

结构是值类型,并且如果它们的每个字段都是可比较的,则可比较。如果两个结构变量的相应字段相等,则认为它们相等。

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")
    }
}

在上面的程序中,namestruct类型包含两个字符串字段。由于字符串具有可比性,因此可以比较两个类型的结构变量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结构类型包含一个字段data,其是类型map。地图是不可比的,因此image1并image2不能相比。如果运行此程序,编译将失败,错误为main.go:18:无效操作:image1 == image2(无法比较包含map [int] int的结构)。