调用的是外层结构体自己定义的版本,不是嵌入结构体的;Go中同名方法遮蔽是编译期静态决定的,外层方法优先覆盖嵌入方法,需显式解引用(如emp.Human.SayHi())才能调用被遮蔽的嵌入方法。

同名方法被遮蔽后,obj.Method() 调用的是哪个?
调用的是外层结构体自己定义的版本,不是嵌入结构体的。Go 没有“重写”或“虚函数”,只有名称解析优先级:外层字段/方法永远覆盖同名的提升方法。
比如 Employee 匿名嵌入 Human,两者都有 SayHi(),那么 emp.SayHi() 一定执行 Employee.SayHi() —— 即使你没写这行代码,只要它存在,就遮蔽了 Human.SayHi()。
- 遮蔽是静态的、编译期决定的,和运行时类型无关
- 接口实现不受影响:
Human的方法仍让Employee满足该接口,但直接调用走的是外层逻辑 - 如果外层没定义同名方法,才轮到提升的嵌入方法生效
想调用被遮蔽的嵌入方法,必须显式解引用
不能靠“父类调用语法”,得手动走到嵌入字段再调用。匿名嵌入时,字段名默认就是类型名(如 Human),所以 emp.Human.SayHi() 才能命中原始逻辑。
- 具名嵌入(如
Person Human)则必须用字段名:emp.Person.SayHi() - 嵌入多层时也一样:若
A嵌入B,B嵌入C,要调C.Method(),得写a.B.C.Method(),不能跳过B - 注意接收者仍是原类型:
emp.Human.SayHi()的 receiver 是*Human,不是*Employee,所以方法体内访问不到emp自身字段
json.Marshal 时同名字段 tag 不会自动继承,容易丢数据
很多人以为嵌入结构体里的 json:"name" 会“透传”到外层,结果序列化出来字段名不对、值为空,甚至整个字段消失——根本原因是 tag 不提升、不继承,每层都得单独配。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 外层字段没加 tag → 默认用字段名转小写下划线(
UserName→user_name) - 内嵌结构体字段即使有 tag,只要没被提升(比如是具名嵌入),tag 就完全无效
-
omitempty必须放在外层字段 tag 上才有用;写在内嵌结构体字段上,对外层零值判断无影响 - 安全做法:所有参与 JSON 序列化的字段,无论内外层,都显式加
json:tag
多层嵌入时方法不会自动穿透,反射也遵循同一规则
Go 只做一级提升,这是硬性限制,不是 bug。反射调用同样受限:reflect.ValueOf(a).MethodByName("CMethod") 会返回 invalid,因为 CMethod 不在 A 的方法集里。
- 想让
a.CMethod()可用,必须在B里手动包装:func (b *B) CMethod() { b.C.CMethod() } - 这种包装不改变接收者类型:
CMethod的 receiver 还是*C,内部不能访问B或A的字段 - 深层嵌套建议重构:要么把
C直接嵌入A,要么用构造函数封装访问路径,避免链式调用暴露实现细节
真正容易被忽略的,是提升规则对反射、接口断言和 JSON 的一致性影响——它不是语法糖,而是语言层面的约束,所有机制都绕不开“匿名 + 导出 = 提升”这个铁律。

















