第四章:结构体与方法
4.1 结构体定义
基本结构体
package main
import "fmt"
type Person struct {
Name string
Age int
Email string
}
type Point struct {
X, Y float64
}
func main() {
// 方式1:按字段顺序初始化
p1 := Person{"张三", 25, "zhangsan@example.com"}
// 方式2:指定字段名(推荐)
p2 := Person{
Name: "李四",
Age: 30,
Email: "lisi@example.com",
}
// 方式3:部分初始化
p3 := Person{Name: "王五"}
// 方式4:new创建(返回指针)
p4 := new(Person)
p4.Name = "赵六"
p4.Age = 35
// 方式5:&取地址
p5 := &Person{Name: "钱七", Age: 40}
fmt.Println(p1)
fmt.Println(p2)
fmt.Println(p3)
fmt.Println(p4)
fmt.Println(p5)
}
结构体嵌套
package main
import "fmt"
type Address struct {
City string
Street string
ZipCode string
}
type Employee struct {
ID int
Name string
Address Address // 嵌套结构体
Salary float64
}
// 匿名嵌套(提升字段)
type Manager struct {
Employee // 匿名嵌套,Employee的字段提升到Manager
Department string
}
func main() {
emp := Employee{
ID: 1,
Name: "张三",
Address: Address{
City: "北京",
Street: "长安街",
ZipCode: "100000",
},
Salary: 15000,
}
// 访问嵌套字段
fmt.Println(emp.Name)
fmt.Println(emp.Address.City)
// 匿名嵌套
mgr := Manager{
Employee: Employee{
ID: 2,
Name: "李四",
},
Department: "技术部",
}
// 可以直接访问Employee的字段
fmt.Println(mgr.Name) // 不需要mgr.Employee.Name
fmt.Println(mgr.Department)
}
4.2 方法
基本方法
package main
import (
"fmt"
"math"
)
type Rectangle struct {
Width float64
Height float64
}
// 值接收者方法
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// 值接收者方法(不改变原值)
func (r Rectangle) Scale(factor float64) Rectangle {
return Rectangle{
Width: r.Width * factor,
Height: r.Height * factor,
}
}
// 指针接收者方法(修改原值)
func (r *Rectangle) ScaleInPlace(factor float64) {
r.Width *= factor
r.Height *= factor
}
// 指针接收者方法(大对象时更高效)
func (r *Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
// 调用方法
fmt.Println("面积:", rect.Area())
fmt.Println("周长:", rect.Perimeter())
// 值接收者返回新对象
scaled := rect.Scale(2)
fmt.Println("原矩形:", rect)
fmt.Println("缩放后:", scaled)
// 指针接收者修改原对象
rect.ScaleInPlace(2)
fmt.Println("原地缩放后:", rect)
}
方法接收者选择
接收者类型 | 使用场景 |
|---|
值接收者 | 小对象,不需要修改原值 |
指针接收者 | 需要修改原值,或大对象避免拷贝 |
// 值接收者 - 小对象
type Point struct {
X, Y float64
}
func (p Point) Distance(other Point) float64 {
dx := p.X - other.X
dy := p.Y - other.Y
return math.Sqrt(dx*dx + dy*dy)
}
// 指针接收者 - 需要修改
type Counter struct {
count int
}
func (c *Counter) Increment() {
c.count++
}
func (c Counter) Value() int {
return c.count
}
4.3 结构体标签
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type User struct {
ID int `json:"id" db:"user_id"`
Username string `json:"username" db:"user_name"`
Email string `json:"email" db:"user_email"`
Password string `json:"-" db:"user_password"` // -表示忽略
}
func main() {
user := User{
ID: 1,
Username: "张三",
Email: "zhangsan@example.com",
Password: "secret",
}
// JSON序列化
data, _ := json.Marshal(user)
fmt.Println(string(data))
// 输出: {"id":1,"username":"张三","email":"zhangsan@example.com"}
// Password被忽略
// 反射读取标签
t := reflect.TypeOf(user)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("字段:%s JSON标签:%s DB标签:%s\n",
field.Name,
field.Tag.Get("json"),
field.Tag.Get("db"))
}
}
4.4 结构体比较
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p1 := Person{Name: "张三", Age: 25}
p2 := Person{Name: "张三", Age: 25}
p3 := Person{Name: "李四", Age: 30}
// 结构体可以直接比较(如果所有字段都可比较)
fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false
// 包含不可比较字段的结构体不能比较
// type Node struct {
// Value int
// Next *Node // 指针可以比较
// }
}
4.5 项目实战:员工管理系统
package main
import "fmt"
// 员工结构体
type Employee struct {
ID int
Name string
Position string
Salary float64
}
// 方法:获取员工信息
func (e Employee) GetInfo() string {
return fmt.Sprintf("ID:%d 姓名:%s 职位:%s 薪资:%.2f",
e.ID, e.Name, e.Position, e.Salary)
}
// 方法:加薪
func (e *Employee) RaiseSalary(percent float64) {
e.Salary *= (1 + percent/100)
}
// 公司结构体
type Company struct {
Name string
Employees []Employee
}
// 方法:添加员工
func (c *Company) AddEmployee(e Employee) {
c.Employees = append(c.Employees, e)
}
// 方法:查找员工
func (c Company) FindEmployee(id int) *Employee {
for i := range c.Employees {
if c.Employees[i].ID == id {
return &c.Employees[i]
}
}
return nil
}
// 方法:计算总薪资
func (c Company) TotalSalary() float64 {
total := 0.0
for _, e := range c.Employees {
total += e.Salary
}
return total
}
func main() {
// 创建公司
company := Company{Name: "科技有限公司"}
// 添加员工
company.AddEmployee(Employee{1, "张三", "工程师", 15000})
company.AddEmployee(Employee{2, "李四", "经理", 25000})
company.AddEmployee(Employee{3, "王五", "设计师", 12000})
// 显示所有员工
fmt.Println("=== 员工列表 ===")
for _, e := range company.Employees {
fmt.Println(e.GetInfo())
}
// 查找并加薪
emp := company.FindEmployee(1)
if emp != nil {
fmt.Printf("\n给 %s 加薪10%%\n", emp.Name)
emp.RaiseSalary(10)
fmt.Println(emp.GetInfo())
}
// 显示总薪资
fmt.Printf("\n公司总薪资: %.2f\n", company.TotalSalary())
}