Go 语言结构体

Go struct
创建于:2020年08月14日 更新于:2020年08月23日

定义

Go语言可以通过自定义的方式形成新的类型,结构体就是这些类型中的一种复合类型,结构体是由零个或多个任意类型的值聚合成的实体,每个值都可以称为结构体的成员。

结构体成员也可以称为“字段”,这些字段有以下特性:

  • 字段拥有自己的类型和值;
  • 字段名必须唯一;
  • 字段的类型也可以是结构体,甚至是字段所在结构体的类型。

使用关键字 type 可以将各种基本类型定义为自定义类型,基本类型包括整型、字符串、布尔等。结构体是一种复合的基本类型,通过 type 定义为自定义类型后,使结构体更便于使用。

type 类型名 struct{
    字段 类型
}

例如:

type Person struct{
    name string
    age int
}

同类型的变量也可以写在一行

type Person struct{
    name, email string
    age int
}

实例化

结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。

实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。

Go语言可以通过多种方式实例化结构体,根据实际需要可以选用不同的写法。

var 实例化方式

package main

import "fmt"

type Person struct{
    name string
    age int
}

func main() {
    var tom = Person
    tom.name = "Tom"
    tom.age = 18

    // var tom = Person{"Tom", 18} 可以省略字段名,但是顺序必须一一对应

    fmt.Println(tom) // {Tom 18}
}

new 实例化方式

package main

import "fmt"

type Person struct{
    name string
    age int
}

func main() {
    tom := new(Person)
    tom.name = "Tom"
    tom.age = 18
    fmt.Println(tom) // &{Tom 18}
}

结构体初始化

使用“键值对”初始化结构体

type Person struct{
    name string
    age int
}

user := Person{name: "Tony", age: 18}

省略键初始化结构体

type Person struct{
    name string
    age int
}

user := Person{"Tony", 18}

初始化匿名结构体
匿名结构体的初始化写法由结构体定义和键值对初始化两部分组成,结构体定义时没有结构体类型名,只有字段和类型定义,键值对初始化部分由可选的多个键值对组成:

init := struct {
    // 匿名结构体字段定义
    字段 字段类型
    …
}{
    // 字段值初始化,注意每行结尾必须有逗号
    初始化字段: 字段的值,
    …
}
package main

import "fmt"

func main() {
    init := struct{
        name string
        age int
    }{
        name: "Tom",
        age: 18,
    }

    fmt.Printf("%v", init) // {Tom 18}
}

匿名字段

Go语言支持只提供类型,而不写字段名的方式,也就是匿名字段,或称为嵌入字段。
当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。

package main

import "fmt"

type Person struct{
    name string
    age int
}

type Tom struct{
    Person
    height int
}

func main() {
    tom := Tom{Person{"Tom", 18}, 180}

    fmt.Printf("%v", tom.name) // Tom

    // 通过Person作为字段名访问
    fmt.Printf("%v", tom.Person.name) // Tom
}

自定义类型和内置类型等都可以作为匿名字段,而且还可以在相应的字段是进行函数操作:

package main

import "fmt"

type Person struct{
    name string
    age int
}

// 类型定义
type Skills []string

type Tom struct{
    Person // 匿名字段,struct
    Skills // 匿名字段,自定义类型 string slice
    height int
    int // 内置类型作为匿名字段
}

func main() {
    tom := Tom{Person:Person{"Tom", 18}, Skills: []string{"computer", "excel"}, height: 180}

    fmt.Printf("his name is:%s\n", tom.name) // he name is:Tom

    // 修改 skill 字段
    tom.Skills = append(tom.Skills, "Python") 

    fmt.Printf("his skills are:%v\n", tom.Skills) // he skills are:[computer excel Python]

    // 修改匿名内置类型
    tom.int = 50

    fmt.Printf("%d", tom.int) // 50
}

如果 Person 里有个 phone 字段,而 Tom 里也有个 phone 字段,当通过 tom.phone 访问,会优先访问最外层的字段

这样就允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。

package main

import "fmt"

type Person struct{
    name , phone string
    age int
}


type Tom struct{
    Person // 匿名字段,struct
    phone string
}

func main() {
    tom := Tom{Person:Person{"Tom", "xxx", 18}, phone: "xxxxxx"}

    fmt.Printf("his phone is:%s\n", tom.phone) // his phone is:xxxxxx

    fmt.Printf("his phone is:%s\n", tom.Person.phone) // his phone is:xxx
}