Go中方法与函数本质不同:方法带接收者并绑定类型,函数无接收者且属于包;值/指针接收者决定能否修改原值;小写字段仅方法内可见;接口实现仅依赖方法而非函数。

Go里方法和函数不是“差不多”,而是编译器眼里完全不同的两类东西:一个带接收者,一个不带;一个绑定到类型,一个属于包。写错就调用不了、实现不了接口、改不了字段——不是报错,是静默失效。
func (p Person) Say() 和 func Say(p Person) 本质不同
前者是方法,后者是函数。区别不在名字或参数,而在接收者声明的位置和语法结构。
-
func (p Person) Say()是方法:接收者(p Person)写在func关键字后、函数名前,且必须带括号和变量名;编译器据此把它加入Person类型的方法集 -
func Say(p Person)是函数:没有接收者部分,p Person是普通参数;它不属于任何类型,不能被p.Say()调用,也不能让Person满足接口 - 常见错误:
func (Person) Say()缺少变量名 → 编译报错missing argument name;func (p *struct{int}) M()用未命名类型作接收者 → 合法性检查失败
值接收者 vs 指针接收者:改不改原值是硬分界
这不是性能建议,是行为契约。接收者类型在定义时就锁死了方法能否修改原始数据。
-
func (p Person) SetName(n string):方法内p.Name = n不影响调用方的person实例,因为传的是整个结构体拷贝 -
func (p *Person) SetName(n string):方法内p.Name = n真实修改原对象;即使你用值变量调用(person.SetName("x")),Go 会自动取地址,但语义仍是“改原值” - 含
slice、map、chan的结构体,哪怕只读也建议用指针接收者——值接收者看似没改字段,但可能误以为slice是深拷贝,实际仍共享底层数组
小写字段在方法里能读写,但对外不可见
字段首字母大小写决定导出性,这直接影响方法能否被跨包调用、JSON 序列化是否包含该字段、ORM 是否映射。
-
type Person { name string; Age int }:方法里可读写p.name和p.Age,但外部包只能访问p.Age,p.name报错cannot refer to unexported field -
json.Marshal(&Person{name: "x", Age: 25})输出{"Age":25},name被忽略;GORM 也不会存name字段 - 嵌入结构体时,只有大写类型才能提升字段:
type Employee struct { Person; Title string }中Person必须是导出类型(首字母大写),否则e.Name编译失败
方法能实现接口,函数永远不能
接口满足判定只看方法集,不看函数签名再像也没用。
- 定义
type Speaker interface { Speak() string },只有func (d Dog) Speak() string这样的方法能让Dog类型自动满足Speaker -
func Speak(d Dog) string即使返回值和参数完全一致,var s Speaker = Dog{}仍报错:cannot use Dog{} (value of type Dog) as Speaker value in assignment: Dog does not implement Speaker - 标准库大量依赖这点:比如
io.Reader接口靠(*os.File).Read实现,不是靠某个包级ReadFile函数
最容易被忽略的是:接收者类型一旦选定,就固定了方法的行为边界。值接收者改不了原值,指针接收者才能改;小写字段在方法里可操作,但对外就是不存在;接口实现只认方法,不认函数——这些不是运行时问题,是定义即锁定的底层契约。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











