Skip to content

Go 语言结构体

结构体是 Go 语言中用于创建自定义数据类型的重要特性。它允许将不同类型的数据组合成一个逻辑单元,是面向对象编程的基础。

📋 结构体基础

结构体定义和初始化

go
package main

import "fmt"

// 定义结构体
type Person struct {
    Name    string
    Age     int
    Email   string
    Height  float64
    IsAdult bool
}

func main() {
    // 1. 零值初始化
    var p1 Person
    fmt.Printf("零值结构体: %+v\n", p1)
    
    // 2. 字段赋值
    p1.Name = "张三"
    p1.Age = 25
    p1.Email = "zhangsan@example.com"
    p1.Height = 1.75
    p1.IsAdult = true
    fmt.Printf("赋值后: %+v\n", p1)
    
    // 3. 结构体字面量初始化
    p2 := Person{
        Name:    "李四",
        Age:     30,
        Email:   "lisi@example.com",
        Height:  1.80,
        IsAdult: true,
    }
    fmt.Printf("字面量初始化: %+v\n", p2)
    
    // 4. 简化初始化(按字段顺序)
    p3 := Person{"王五", 28, "wangwu@example.com", 1.70, true}
    fmt.Printf("顺序初始化: %+v\n", p3)
    
    // 5. 部分初始化
    p4 := Person{
        Name: "赵六",
        Age:  22,
    }
    fmt.Printf("部分初始化: %+v\n", p4)
}

匿名结构体

go
func main() {
    // 匿名结构体定义和使用
    student := struct {
        ID     int
        Name   string
        Grades []int
    }{
        ID:     1001,
        Name:   "学生A",
        Grades: []int{85, 92, 78, 96},
    }
    
    fmt.Printf("匿名结构体: %+v\n", student)
    
    // 函数中使用匿名结构体
    config := struct {
        Host string
        Port int
        SSL  bool
    }{"localhost", 8080, false}
    
    fmt.Printf("配置: %+v\n", config)
    
    // 切片中的匿名结构体
    users := []struct {
        Name string
        Role string
    }{
        {"管理员", "admin"},
        {"用户1", "user"},
        {"用户2", "user"},
    }
    
    for _, user := range users {
        fmt.Printf("用户: %s, 角色: %s\n", user.Name, user.Role)
    }
}

🔧 结构体方法

方法定义

go
import (
    "fmt"
    "math"
)

type Rectangle struct {
    Width  float64
    Height float64
}

// 值接收者方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 值接收者方法
func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// 指针接收者方法
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

// 指针接收者方法
func (r *Rectangle) SetDimensions(width, height float64) {
    r.Width = width
    r.Height = height
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    
    fmt.Printf("原始矩形: %+v\n", rect)
    fmt.Printf("面积: %.2f\n", rect.Area())
    fmt.Printf("周长: %.2f\n", rect.Perimeter())
    
    // 调用指针接收者方法
    rect.Scale(2)
    fmt.Printf("放大2倍后: %+v\n", rect)
    fmt.Printf("新面积: %.2f\n", rect.Area())
    
    // 设置新尺寸
    rect.SetDimensions(8, 6)
    fmt.Printf("设置新尺寸后: %+v\n", rect)
}

值接收者 vs 指针接收者

go
type Counter struct {
    count int
}

// 值接收者:不会修改原始结构体
func (c Counter) IncrementValue() {
    c.count++
    fmt.Printf("值接收者内部: %d\n", c.count)
}

// 指针接收者:会修改原始结构体
func (c *Counter) IncrementPointer() {
    c.count++
    fmt.Printf("指针接收者内部: %d\n", c.count)
}

// 值接收者:获取当前值
func (c Counter) GetCount() int {
    return c.count
}

func main() {
    counter := Counter{count: 0}
    
    fmt.Printf("初始值: %d\n", counter.GetCount())
    
    // 值接收者调用
    counter.IncrementValue()
    fmt.Printf("值接收者调用后: %d\n", counter.GetCount())
    
    // 指针接收者调用
    counter.IncrementPointer()
    fmt.Printf("指针接收者调用后: %d\n", counter.GetCount())
    
    // 继续调用指针接收者
    counter.IncrementPointer()
    fmt.Printf("再次指针接收者调用后: %d\n", counter.GetCount())
}

🏗️ 结构体嵌套

嵌套结构体

go
type Address struct {
    Street   string
    City     string
    Province string
    ZipCode  string
}

type Contact struct {
    Phone string
    Email string
}

type Employee struct {
    ID       int
    Name     string
    Address  Address  // 嵌套结构体
    Contact  Contact  // 嵌套结构体
    Salary   float64
    IsActive bool
}

func main() {
    emp := Employee{
        ID:   1001,
        Name: "张三",
        Address: Address{
            Street:   "中关村大街1号",
            City:     "北京",
            Province: "北京市",
            ZipCode:  "100000",
        },
        Contact: Contact{
            Phone: "13800138000",
            Email: "zhangsan@company.com",
        },
        Salary:   8000.0,
        IsActive: true,
    }
    
    fmt.Printf("员工信息: %+v\n", emp)
    
    // 访问嵌套字段
    fmt.Printf("员工姓名: %s\n", emp.Name)
    fmt.Printf("员工城市: %s\n", emp.Address.City)
    fmt.Printf("员工邮箱: %s\n", emp.Contact.Email)
    
    // 修改嵌套字段
    emp.Address.City = "上海"
    emp.Contact.Phone = "13900139000"
    
    fmt.Printf("修改后地址: %s\n", emp.Address.City)
    fmt.Printf("修改后电话: %s\n", emp.Contact.Phone)
}

匿名字段(嵌入)

go
type Person struct {
    Name string
    Age  int
}

// Person 方法
func (p Person) Introduce() {
    fmt.Printf("我是 %s,今年 %d\n", p.Name, p.Age)
}

type Student struct {
    Person    // 匿名字段(嵌入)
    StudentID string
    Major     string
}

// Student 特有方法
func (s Student) Study() {
    fmt.Printf("%s 正在学习 %s\n", s.Name, s.Major)
}

type Teacher struct {
    Person     // 匿名字段(嵌入)
    EmployeeID string
    Subject    string
}

// Teacher 特有方法
func (t Teacher) Teach() {
    fmt.Printf("%s 正在教授 %s\n", t.Name, t.Subject)
}

func main() {
    // 创建学生
    student := Student{
        Person: Person{
            Name: "小明",
            Age:  20,
        },
        StudentID: "S001",
        Major:     "计算机科学",
    }
    
    // 直接访问嵌入字段
    fmt.Printf("学生姓名: %s\n", student.Name)  // 直接访问,等价于 student.Person.Name
    fmt.Printf("学生年龄: %d\n", student.Age)
    fmt.Printf("学号: %s\n", student.StudentID)
    
    // 调用嵌入类型的方法
    student.Introduce()  // 调用 Person 的方法
    student.Study()      // 调用 Student 的方法
    
    // 创建教师
    teacher := Teacher{
        Person: Person{
            Name: "王老师",
            Age:  35,
        },
        EmployeeID: "T001",
        Subject:    "数学",
    }
    
    teacher.Introduce()  // 调用 Person 的方法
    teacher.Teach()      // 调用 Teacher 的方法
}

🔗 结构体标签

JSON 标签

go
import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    Password string `json:"-"`                    // 忽略字段
    Age      int    `json:"age,omitempty"`        // 空值时忽略
    IsActive bool   `json:"is_active"`
    Profile  string `json:"profile,omitempty"`
}

func main() {
    user := User{
        ID:       1,
        Name:     "张三",
        Email:    "zhangsan@example.com",
        Password: "secret123",
        Age:      0,  // 空值
        IsActive: true,
        // Profile 未设置
    }
    
    // 结构体转 JSON
    jsonData, err := json.Marshal(user)
    if err != nil {
        fmt.Printf("JSON 序列化错误: %v\n", err)
        return
    }
    
    fmt.Printf("JSON 数据: %s\n", jsonData)
    
    // JSON 转结构体
    jsonStr := `{"id":2,"name":"李四","email":"lisi@example.com","age":25,"is_active":false,"profile":"软件工程师"}`
    
    var newUser User
    err = json.Unmarshal([]byte(jsonStr), &newUser)
    if err != nil {
        fmt.Printf("JSON 反序列化错误: %v\n", err)
        return
    }
    
    fmt.Printf("反序列化结果: %+v\n", newUser)
}

验证标签

go
import (
    "fmt"
    "reflect"
    "strconv"
    "strings"
)

type Product struct {
    ID          int     `validate:"required,min:1"`
    Name        string  `validate:"required,max:100"`
    Price       float64 `validate:"required,min:0"`
    Category    string  `validate:"required"`
    Description string  `validate:"max:500"`
}

// 简单的验证器
func validateStruct(s interface{}) []string {
    var errors []string
    v := reflect.ValueOf(s)
    t := reflect.TypeOf(s)
    
    for i := 0; i < v.NumField(); i++ {
        field := v.Field(i)
        fieldType := t.Field(i)
        tag := fieldType.Tag.Get("validate")
        
        if tag == "" {
            continue
        }
        
        rules := strings.Split(tag, ",")
        fieldName := fieldType.Name
        
        for _, rule := range rules {
            err := validateField(fieldName, field, rule)
            if err != "" {
                errors = append(errors, err)
            }
        }
    }
    
    return errors
}

func validateField(fieldName string, field reflect.Value, rule string) string {
    switch {
    case rule == "required":
        if field.Kind() == reflect.String && field.String() == "" {
            return fmt.Sprintf("%s is required", fieldName)
        }
        if field.Kind() == reflect.Int && field.Int() == 0 {
            return fmt.Sprintf("%s is required", fieldName)
        }
        if field.Kind() == reflect.Float64 && field.Float() == 0 {
            return fmt.Sprintf("%s is required", fieldName)
        }
    
    case strings.HasPrefix(rule, "min:"):
        minStr := strings.TrimPrefix(rule, "min:")
        min, _ := strconv.Atoi(minStr)
        if field.Kind() == reflect.Int && field.Int() < int64(min) {
            return fmt.Sprintf("%s must be at least %d", fieldName, min)
        }
        if field.Kind() == reflect.Float64 && field.Float() < float64(min) {
            return fmt.Sprintf("%s must be at least %d", fieldName, min)
        }
    
    case strings.HasPrefix(rule, "max:"):
        maxStr := strings.TrimPrefix(rule, "max:")
        max, _ := strconv.Atoi(maxStr)
        if field.Kind() == reflect.String && len(field.String()) > max {
            return fmt.Sprintf("%s must be at most %d characters", fieldName, max)
        }
    }
    
    return ""
}

func main() {
    // 有效产品
    validProduct := Product{
        ID:          1,
        Name:        "笔记本电脑",
        Price:       5999.99,
        Category:    "电子产品",
        Description: "高性能笔记本电脑",
    }
    
    errors := validateStruct(validProduct)
    if len(errors) == 0 {
        fmt.Println("有效产品验证通过")
    } else {
        fmt.Printf("验证错误: %v\n", errors)
    }
    
    // 无效产品
    invalidProduct := Product{
        ID:          0,  // 违反 min:1
        Name:        "",  // 违反 required
        Price:       -100,  // 违反 min:0
        Category:    "电子产品",
        Description: "",
    }
    
    errors = validateStruct(invalidProduct)
    if len(errors) > 0 {
        fmt.Println("无效产品验证错误:")
        for _, err := range errors {
            fmt.Printf("  - %s\n", err)
        }
    }
}

🎯 实际应用示例

学生管理系统

go
import (
    "fmt"
    "time"
)

// 成绩结构体
type Grade struct {
    Subject string
    Score   float64
    Date    time.Time
}

// 学生结构体
type Student struct {
    ID       string
    Name     string
    Age      int
    Email    string
    Grades   []Grade
    GPA      float64
}

// 计算 GPA
func (s *Student) CalculateGPA() {
    if len(s.Grades) == 0 {
        s.GPA = 0
        return
    }
    
    total := 0.0
    for _, grade := range s.Grades {
        total += grade.Score
    }
    s.GPA = total / float64(len(s.Grades))
}

// 添加成绩
func (s *Student) AddGrade(subject string, score float64) {
    grade := Grade{
        Subject: subject,
        Score:   score,
        Date:    time.Now(),
    }
    s.Grades = append(s.Grades, grade)
    s.CalculateGPA()
}

// 获取最高分
func (s Student) GetHighestGrade() (Grade, bool) {
    if len(s.Grades) == 0 {
        return Grade{}, false
    }
    
    highest := s.Grades[0]
    for _, grade := range s.Grades[1:] {
        if grade.Score > highest.Score {
            highest = grade
        }
    }
    return highest, true
}

// 学生信息显示
func (s Student) DisplayInfo() {
    fmt.Printf("=== 学生信息 ===\n")
    fmt.Printf("学号: %s\n", s.ID)
    fmt.Printf("姓名: %s\n", s.Name)
    fmt.Printf("年龄: %d\n", s.Age)
    fmt.Printf("邮箱: %s\n", s.Email)
    fmt.Printf("GPA: %.2f\n", s.GPA)
    
    fmt.Printf("成绩列表:\n")
    for _, grade := range s.Grades {
        fmt.Printf("  %s: %.1f (日期: %s)\n", 
                   grade.Subject, grade.Score, grade.Date.Format("2006-01-02"))
    }
    
    if highest, ok := s.GetHighestGrade(); ok {
        fmt.Printf("最高分: %s %.1f\n", highest.Subject, highest.Score)
    }
}

// 班级结构体
type Class struct {
    Name     string
    Students []Student
}

// 添加学生
func (c *Class) AddStudent(student Student) {
    c.Students = append(c.Students, student)
}

// 获取班级平均 GPA
func (c Class) GetAverageGPA() float64 {
    if len(c.Students) == 0 {
        return 0
    }
    
    total := 0.0
    for _, student := range c.Students {
        total += student.GPA
    }
    return total / float64(len(c.Students))
}

// 获取最优学生
func (c Class) GetTopStudent() (Student, bool) {
    if len(c.Students) == 0 {
        return Student{}, false
    }
    
    top := c.Students[0]
    for _, student := range c.Students[1:] {
        if student.GPA > top.GPA {
            top = student
        }
    }
    return top, true
}

func main() {
    // 创建学生
    student1 := Student{
        ID:    "S001",
        Name:  "张三",
        Age:   20,
        Email: "zhangsan@university.edu",
    }
    
    student2 := Student{
        ID:    "S002",
        Name:  "李四",
        Age:   21,
        Email: "lisi@university.edu",
    }
    
    // 添加成绩
    student1.AddGrade("数学", 95)
    student1.AddGrade("英语", 88)
    student1.AddGrade("计算机", 92)
    
    student2.AddGrade("数学", 87)
    student2.AddGrade("英语", 94)
    student2.AddGrade("计算机", 89)
    
    // 显示学生信息
    student1.DisplayInfo()
    fmt.Println()
    student2.DisplayInfo()
    
    // 创建班级
    class := Class{Name: "计算机科学2021级"}
    class.AddStudent(student1)
    class.AddStudent(student2)
    
    fmt.Printf("\n=== 班级统计 ===\n")
    fmt.Printf("班级: %s\n", class.Name)
    fmt.Printf("学生人数: %d\n", len(class.Students))
    fmt.Printf("班级平均GPA: %.2f\n", class.GetAverageGPA())
    
    if top, ok := class.GetTopStudent(); ok {
        fmt.Printf("班级第一名: %s (GPA: %.2f)\n", top.Name, top.GPA)
    }
}

🎓 小结

本章我们全面学习了 Go 语言的结构体:

  • 结构体基础:定义、初始化、匿名结构体
  • 结构体方法:值接收者、指针接收者
  • 结构体嵌套:嵌套结构体、匿名字段
  • 结构体标签:JSON 标签、验证标签
  • 实际应用:学生管理系统实现

结构体是 Go 语言面向对象编程的基础,掌握结构体的使用对于构建复杂应用程序至关重要。


接下来,我们将学习 Go 语言集合(Map),了解键值对数据结构的使用。

结构体使用建议

  • 选择合适的接收者类型(值 vs 指针)
  • 合理使用结构体嵌套和组合
  • 利用结构体标签进行数据验证和序列化
  • 为结构体设计清晰的方法接口

本站内容仅供学习和研究使用。