golang的接口的指针接收者和非指针接收者的区别(notify method has pointer receiver)

本文介绍了在Golang中遇到的一个问题:不能将非指针类型的struct赋值给有特定方法(指针接收者)的接口。解释了Golang中方法集合的概念,值类型和指针类型对象拥有不同的方法集。指针类型的receiver方法仅由指针类型的对象实现,导致非指针类型无法满足接口要求。通过示例展示了如何修改代码以正确实现接口。

在Golang中第一次使用interface 遇到了一个有意思的问题:

cannot use serialServiceEntity (type serialService) as type service in assignment:
serialService does not implement service (Close method has pointer receiver)
1
这个问题很普遍,所以在此记录先来。
先看以下例子:

package main
import (
    "fmt"
)

// notifier is an interface that defined
// type behavior.

type notifier interface {
   
   
    notify()
}

// user defines a user in the program.

type user struct {
   
   
    name  string
    email string
}

// notify implements a method with a poi
func (u *user) notify() {
   
      
    fmt.Printf("Sending user email to %s“,u.name)
}

// main is the entry point for the appli
func main() {

    // Create a value of type User and s
    u := user{"Bill", "bill@email.com"
<think>嗯,用户想了解Go语言中值接收者指针接收者的一般使用场景。根据提供的引用资料可以看出,这个问题涉及到方法定义的本质差异(值复制 vs 地址操作)、接口实现规则、以及编译器自动转换机制这三层核心内容。 用户似乎已经看过基础示例,现在需要的是决策依据。引用[1]提到“正确选择对性能维护性至关重要”,这提示我需要突出实际工程场景的权衡。注意到用户引用的四个资料中,引用[2]的代码示例特别典型——它展示了编译器对c.Increment()的自动取地址行为,这点在讲解使用场景时一定要强调。 用户可能是中级Go开发者,因为ta跳过了“是什么”直接问“怎么选”。不过引用[4]的接口实现示例显示用户可能对指针接收者接口限制存在困惑,这点需要明确说明:当实现接口的方法使用指针接收者时,只有类型的指针才能赋值给接口变量。 从性能角度,准备用切片增长作为例子说明指针接收者优势;从并发安全角度,要强调值接收者在只读场景的线程安全性。意外发现引用[3]的例子很适合解释修改需求时的指针必要性——那个ChangeValue方法修改了接收者的状态。 用户没问但可能关心的隐藏问题是:为什么编译器要设计自动取地址/解引用的语法糖?这其实关系到Go语言“尽量让指针操作透明化”的设计哲学,可以在总结部分点明。 </think>在Go语言中,**值接收者****指针接收者**的选择取决于具体场景,以下是核心原则使用场景分析: --- ### **一、一般原则** 1. **默认推荐指针接收者**(尤其需修改接收者状态时) 2. **值接收者**适用于无需修改状态的小对象或不可变类型 --- ### **二、值接收者使用场景** 1. **不修改接收者状态** 仅读取数据,不改变结构体字段时: ```go type Point struct{ X, Y float64 } func (p Point) Distance() float64 { return math.Sqrt(p.X*p.X + p.Y*p.Y) // 不修改p } ``` 2. **小型结构体(拷贝成本低)** 结构体字段少且为基本类型(如坐标、颜色RGBA值) 3. **要求并发安全** 方法内通过值副本操作,避免并发修改竞争: ```go type ImmutableConfig struct{ config string } func (c ImmutableConfig) Get() string { return c.config // 值副本保证读取安全 } ``` 4. **实现标准库接口(如`fmt.Stringer`)** 若接口方法不要求修改接收者,优先用值接收者: ```go func (p Point) String() string { return fmt.Sprintf("(%v,%v)", p.X, p.Y) } ``` --- ### **三、指针接收者使用场景** 1. **需修改接收者状态** 必须修改结构体字段时: ```go type Counter struct{ count int } func (c *Counter) Increment() { c.count++ // 修改原对象 } ``` 2. **结构体较大(避免拷贝开销)** 包含数组或嵌套结构的复杂类型: ```go type BigData struct{ data [10000]int } func (b *BigData) Process() { ... } // 避免拷贝10,000个元素 ``` 3. **实现接口时需统一类型** 若接口方法需指针接收者,则实例必须取地址(值类型会导致编译错误)[^4]: ```go type Speaker interface { Speak() } type Dog struct{} func (d *Dog) Speak() {} // 指针接收者 func main() { var s Speaker s = Dog{} // 错误!需改为 &Dog{} s = &Dog{} // 正确 } ``` 4. **方法链调用(Method Chaining)** 需连续修改对象状态: ```go type Builder struct{ buffer strings.Builder } func (b *Builder) Write(s string) *Builder { b.buffer.WriteString(s) return b // 返回指针支持链式调用 } ``` --- ### **四、关键注意事项** 1. **编译器自动转换** Go会对`(&obj).ValueMethod()``(*ptr).PointerMethod()`做隐式转换[^2],但接口实现时需严格匹配类型[^4]。 2. **零值可用性** 指针接收者需处理`nil`情况: ```go func (c *Counter) Increment() { if c == nil { return } // 防御nil指针 c.count++ } ``` 3. **一致性原则** 同一类型的所有方法应统一使用值或指针接收者,避免混淆[^1]。 --- ### **五、决策流程图** ```mermaid graph TD A[需修改接收者状态?] -->|Yes| B[指针接收者] A -->|No| C[结构体是否很大?] C -->|Yes| B C -->|No| D[需实现接口接口方法为指针?] D -->|Yes| B D -->|No| E[值接收者] ```
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值