Go学习笔记04-结构体

结构体的声明

  • 命名结构体,如下创建了名为Employee的新类型,而它可用于创建Employee类型的结构体变量。
type Employee struct {
    firstName, lastName string
    age, salary int
}
  • 匿名结构体,即声明结构体时可以不用声明一个新类型。
var employee struct {
    firstName, lastName string
    age int
}

创建命名的结构体

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

func main() {
    emp1 := Employee{
        firstName: "Sam",
        age:       25,
        salary:    500,
        lastName:  "Anderson",
    }
    emp2 := Employee{"Thomas", "Paul", 29, 800}
}

上述代码,首先创建了一个命名的结构体Employee。然后通过指定每个字段名的值,定义了结构体变量emp1,字段名的顺序不一定要与声明结构体类型时的顺序相同。在定义emp2时省略了字段名,在这种情况下,就需要保证字段名的顺序与声明结构体时的顺序相同。

创建匿名结构体

func main() {
    emp3 := struct {
        firstName, lastName string
        age, salary int
    }{
        firstName: "Andreah",
        lastName:  "Nikola",
        age:       31,
        salary:    5000,
    }
}

上述代码定义了一个匿名结构体变量emp3,它只是创建了一个新的结构体变量,而没有定义任何结构体类型。

结构体的零值

  • 当定义好的结构体并没有被显示地初始化时,该结构体的字段将默认赋值为零值。
  • 还可以为某些字段指定初始值,而忽略其它字段,这样,忽略的字段名会赋值为零值。

访问结构体的字段

  • 点号操作符.用于访问结构体的字段。

结构体的指针

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

func main() {
    emp4 := &Employee{"Sam", "Anderson", 55, 6000}
    
    fmt.Println("FirstName:", (*emp4).firstName)
    fmt.Println("Age:", (*emp4).age)
    // Go允许我们在访问字段时,使用 emp4.lastName 来代替显示的解引用 (*emp4).lastName
    fmt.Println("LastName:", emp4.lastName)
    fmt.Println("Salary:", emp4.salary)
}

匿名字段

当我们创建结构体时,字段可以只有类型,而没有字段名。这样的字段称为匿名字段。举个例子:

// 创建了一个Person结构体,含有两个匿名字段string和int
type Person struct {
    string
    int
}

func main() {
    p := Person{"Naveen", 50}
    fmt.Println(p) // {Naveen 50}
    
    // 虽然匿名字段没有名称,但其实匿名字段的名称就默认为它的类型
    // 所以不能含有两个相同类型的匿名字段
    p.string = "Steve"
    p.int = 45
    fmt.Println(p) // {Steve 45}
}

嵌套结构体

结构体的字段有可能也是一个结构体,这样的结构体称为嵌套结构体。

type Address struct {
    city string
}

type Person1 struct {
    name string
    age int
    address Address
}

// 如果结构体中有匿名的结构体类型字段,则该匿名结构体里的字段就称为提升字段
// 提升字段就像是属于外部结构体一样,可以用外部结构体直接访问
type Person2 struct {
    name string
    age int
    Address
}

func main() {
    var p1 Person1
    p1.name = "Naveen"
    p1.age = 50
    p1.address = Address{
        city: "Chicago",
    }
    fmt.Println("Name:", p1.name) // Name: Naveen
    fmt.Println("Age:", p1.age) // Age: 50
    fmt.Println("City:", p1.address.city) // City: Chicago

    var p2 Person2
    p2.name = "Steve"
    p2.age = 45
    p2.Address = Address{
        city: "New York",
    }
    fmt.Println("Name:", p2.name) // Name: Steve
    fmt.Println("Age:", p2.age) // Age: 45
    fmt.Println("City:", p2.city) // City: New York
}

结构体相等性

结构体是值类型。如果它的每一个字段都是可比较的,则该结构体也是可比较的。如果两个结构体变量对应的字段相等,则这两个变量也是相等的。如果结构体包含不可比较的字段,则结构体变量也不可比较。

结构体取代类

Go不支持类,而是提供了结构体,结构体中可以添加方法,这样就可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。

可以通过提供参数化的构造器来隐藏类型,避免从其它包直接访问。通常做法:对应一个结构体应该提供一种名为NewT(parameters)的函数,按照要求来初始化T类型的变量。按照Go的惯例,如果一个包只含有一种类型,应该把函数命名为New(parameters)而不是NewT(parameters)。例子如下:

package employee

import "fmt"

// 隐藏的类型
type employee struct {
    firstName   string
    lastName    string
    totalLeaves int
    leavesTaken int
}

// 这里New是使用employee的唯一办法,安全可靠
func New(firstName string, lastName string, totalLeaves int, leavesTaken int) employee {
    e := employee{firstName, lastName, totalLeaves, leavesTaken}
    return e
}

// 注意,导出方法要大写
func (e employee) LeavesRemaining() {
    fmt.Printf("%s %s has %d leaves remaining",
        e.firstName, e.lastName, e.totalLeaves-e.leavesTaken)
}

reference:

https://studygolang.com/articles/12263
https://studygolang.com/articles/12630