go从零单排之方法
一、Go 方法Go 中的方法Method是「绑定到特定类型的函数」可以把它理解为给自定义类型结构体 / 基本类型“新增” 的专属函数核心作用是让代码更符合面向对象的 “封装” 思想同时保持 Go 语言的简洁性。1. 方法与函数的核心区别特性函数Function方法Method绑定关系无绑定属于全局 / 包级别绑定到特定类型接收者属于该类型定义语法func 函数名(参数) 返回值 {}func (接收者) 方法名(参数) 返回值 {}调用方式函数名(参数)实例.方法名(参数)2. 方法示例package main import fmt // 1. 定义自定义类型以结构体为例 type Person struct { Name string Age int } // 2. 为 Person 类型定义方法值接收者 // (p Person) 是接收者表示该方法绑定到 Person 类型 func (p Person) SayHello() { fmt.Printf(大家好我是%s今年%d岁\n, p.Name, p.Age) } // 3. 为 Person 定义修改属性的方法指针接收者 func (p *Person) Grow() { p.Age // 修改接收者的属性 } func main() { // 4. 创建实例并调用方法 p : Person{Name: 张三, Age: 20} p.SayHello() // 输出大家好我是张三今年20岁 p.Grow() // 调用指针接收者方法修改Age p.SayHello() // 输出大家好我是张三今年21岁 }关键拆解接收者Receiver方法定义中 (p Person) 或 (p *Person) 是核心p 是接收者变量可自定义命名通常用类型首字母小写Person/*Person 是接收者类型调用逻辑方法必须通过「类型实例」调用不能像函数一样直接调用核心价值把 “数据结构体” 和 “操作数据的逻辑方法” 绑定实现封装。3. 接收者的两种类型值接收者 vs 指针接收者这是 Go 方法最核心的知识点直接决定方法能否修改原实例接收者类型语法示例本质能否修改原实例适用场景值接收者(p Person)接收实例的拷贝不能只读操作、简单类型、避免拷贝指针接收者(p *Person)接收实例的指针能修改实例、大结构体、避免拷贝实战对比示例package main import fmt type Student struct { Score int } // 值接收者操作的是拷贝原实例不变 func (s Student) AddScore1(score int) { s.Score score } // 指针接收者操作的是原实例会修改值 func (s *Student) AddScore2(score int) { s.Score score } func main() { stu : Student{Score: 80} stu.AddScore1(10) fmt.Println(stu.Score) // 输出 80值接收者原实例未变 stu.AddScore2(10) fmt.Println(stu.Score) // 输出 90指针接收者原实例修改 }重要补充Go 会自动处理值和指针的转换调用方法时无需手动取地址 / 解引用值实例调用指针接收者方法stu.AddScore2(10) 等价于 (stu).AddScore2(10)指针实例调用值接收者方法(stu).AddScore1(10) 等价于 stu.AddScore1(10)。4. 为非结构体类型定义方法Go 允许为「自定义的基本类型别名」定义方法不能直接为原生基本类型如 int 定义package main import fmt // 定义int的别名类型 type MyInt int // 为MyInt定义方法 func (m MyInt) Double() MyInt { return m * 2 } func main() { var num MyInt 5 fmt.Println(num.Double()) // 输出 10 }二、Go 方法的核心特性1. 封装性面向对象核心方法可以访问接收者的私有字段结构体中首字母小写的字段外部包无法直接访问只能通过方法操作实现数据封装// model包 - model/person.go package model // Person 结构体导出 type Person struct { Name string // 导出字段首字母大写 age int // 私有字段首字母小写 } // SetAge 方法导出外部包只能通过该方法修改age func (p *Person) SetAge(a int) { if a 0 a 150 { // 增加校验逻辑 p.age a } } // GetAge 方法导出外部包只能通过该方法读取age func (p *Person) GetAge() int { return p.age } // main包 package main import ( fmt your-project/model ) func main() { p : model.Person{Name: 李四} p.SetAge(25) // 合法通过方法修改私有字段 fmt.Println(p.GetAge()) // 输出 25 // p.age 30 // 编译错误无法访问私有字段 }2. 方法集Method Set每个类型都有对应的「方法集」决定该类型的实例 / 指针能调用哪些方法值类型T的方法集包含所有「值接收者」方法*指针类型T的方法集包含所有「值接收者 指针接收者」方法通俗理解指针类型能调用所有方法值类型只能调用值接收者方法Go 会自动转换但反向不行。3. 方法重载NoGo 不支持方法重载同一类型不能定义同名但参数不同的方法这是 Go 简洁性的体现若需不同逻辑可通过参数可选、多返回值实现。4. 方法与接口的配合核心应用方法是实现接口的唯一方式只要类型实现了接口的所有方法就隐式实现了该接口无需显式声明Go 接口的 “鸭子类型”package main import fmt // 定义接口 type Speaker interface { Speak() string } // 定义类型1 type Dog struct{} // 实现Speaker接口的Speak方法 func (d Dog) Speak() string { return 汪汪汪 } // 定义类型2 type Cat struct{} // 实现Speaker接口的Speak方法 func (c Cat) Speak() string { return 喵喵喵 } // 通用函数接收Speaker接口 func MakeSound(s Speaker) { fmt.Println(s.Speak()) } func main() { MakeSound(Dog{}) // 输出 汪汪汪 MakeSound(Cat{}) // 输出 喵喵喵 }三、Go 方法的典型业务场景场景 1结构体封装最核心场景为业务实体如用户、订单、商品定义方法封装数据和操作逻辑package main import ( fmt time ) // 订单结构体 type Order struct { OrderID string Amount float64 Status string // 状态pending/payed/shipped/completed CreateTime time.Time } // 支付订单指针接收者修改状态 func (o *Order) Pay() error { if o.Status ! pending { return fmt.Errorf(订单%s状态异常无法支付, o.OrderID) } o.Status payed return nil } // 发货指针接收者 func (o *Order) Ship() error { if o.Status ! payed { return fmt.Errorf(订单%s未支付无法发货, o.OrderID) } o.Status shipped return nil } // 获取订单创建时间字符串值接收者只读 func (o Order) GetCreateTime() string { return o.CreateTime.Format(2006-01-02 15:04:05) } func main() { order : Order{ OrderID: ORD20260320001, Amount: 99.9, Status: pending, CreateTime: time.Now(), } if err : order.Pay(); err ! nil { fmt.Println(err) } else { fmt.Println(支付成功订单状态, order.Status) // 输出 payed } fmt.Println(订单创建时间, order.GetCreateTime()) }场景 2工具类封装基本类型扩展为基本类型别名定义方法实现工具类逻辑如字符串、数字的常用操作package main import strings // 字符串别名 type MyString string // 去除空格并转小写 func (s MyString) TrimAndLower() MyString { return MyString(strings.TrimSpace(string(s))) } // 判断是否包含子串 func (s MyString) Contains(sub string) bool { return strings.Contains(string(s), sub) } func main() { var str MyString Hello World fmt.Println(str.TrimAndLower()) // 输出 hello world fmt.Println(str.Contains(World)) // 输出 true }场景 3接口实现多态通过方法实现接口实现多态逻辑如不同数据源的读取、不同支付方式的处理package main import fmt // 支付接口 type Payment interface { Pay(amount float64) string } // 微信支付 type WeChatPay struct{} func (w WeChatPay) Pay(amount float64) string { return fmt.Sprintf(微信支付%.2f元成功, amount) } // 支付宝支付 type AliPay struct{} func (a AliPay) Pay(amount float64) string { return fmt.Sprintf(支付宝支付%.2f元成功, amount) } // 统一支付函数 func UnifiedPay(p Payment, amount float64) { fmt.Println(p.Pay(amount)) } func main() { UnifiedPay(WeChatPay{}, 100.0) // 输出 微信支付100.00元成功 UnifiedPay(AliPay{}, 200.0) // 输出 支付宝支付200.00元成功 }场景 4链式调用方法返回接收者本身指针实现链式调用package main import fmt type Builder struct { content string } func (b *Builder) AddTitle(title string) *Builder { b.content fmt.Sprintf(h1%s/h1, title) return b // 返回指针支持链式调用 } func (b *Builder) AddContent(content string) *Builder { b.content fmt.Sprintf(p%s/p, content) return b } func (b *Builder) Build() string { return b.content } func main() { // 链式调用简洁直观 html : (Builder{}). AddTitle(Go方法教程). AddContent(方法是绑定到类型的函数). Build() fmt.Println(html) // 输出h1Go方法教程/h1p方法是绑定到类型的函数/p }总结方法本质绑定到特定类型的函数核心是「接收者」分为值接收者只读和指针接收者可修改核心特性支持封装、配合接口实现多态无方法重载指针类型方法集包含所有方法典型场景结构体业务建模、工具类扩展、接口实现、链式调用是 Go 面向对象编程的核心载体。Go 方法的设计既保留了函数的简洁性又实现了面向对象的核心思想封装、多态是日常开发中处理 “数据 逻辑” 的最优方式。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2437454.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!