Go结构体嵌套是组合而非继承;仅匿名嵌入+导出字段才支持字段/方法一级提升,JSON tag不继承,多层嵌套需显式访问或手动转发。

Go 里 struct 嵌套不是继承,是组合;只有匿名嵌入 + 导出字段才能“透出”字段和方法,否则必须写 u.Addr.City 这种显式路径。
匿名嵌入才能触发字段提升
字段提升(promoted fields)只在匿名嵌入时发生:结构体字段只写类型,不写字段名。内层结构体的导出字段(如 City string)会自动出现在外层作用域中。
-
type User struct { Address }→u.City合法(前提是Address里有导出字段City string) -
type User struct { Addr Address }→u.City编译失败,只能写u.Addr.City -
type Address struct { city string }(小写)→ 即使匿名嵌入,u.city也不可见,包外无法访问,json.Marshal也会丢该字段
方法只做一级提升,不穿透多层
Go 不支持方法链式穿透。A 匿名嵌入 B,B 匿名嵌入 C,那么 A 可以直接调用 B 的导出方法,但不能直接调用 C 的方法——哪怕 C 的方法已导出。
-
type A struct { B }; type B struct { C }→a.CMethod()会编译失败 - 可行方案:在
B中加转发方法func (b *B) CMethod() { b.C.CMethod() },这样a.CMethod()才能成立 - 反射也遵循这规则:
reflect.ValueOf(a).MethodByName("CMethod")同样无效,除非CMethod已被B显式暴露 - 注意:方法接收者类型不变——
CMethod的 receiver 还是*C,不是*A
JSON tag 不继承,每层都得自己标
json tag 不会从内层自动“冒泡”到外层。哪怕 Address 字段已经打了 json:"address",它里面的 City 字段如果没标,照样按默认名(City)输出,而不是你想要的 city。
立即学习“go语言免费学习笔记(深入)”;
- 正确写法:
type Address struct { City string `json:"city"` }+type User struct { Address Address `json:"address"` } - 常见漏标:
Address结构体本身没加 tag,或字段用了小写名(city string)导致 JSON 里根本不出现在结果中 -
omitempty放在内嵌结构体上,但外层是具名字段(如Profile Profile),那Profile为零值时不会被忽略——必须在外层字段 tag 里显式写json:",omitempty"
匿名结构体切片常用于表驱动测试
像 []struct{in, want string} 这种写法,本质是定义一个元素类型为匿名结构体的切片,适合一次性、局部使用的测试数据组织。
-
cases := []struct{in, want string}{{"a", "A"}, {"b", "B"}}→ 每个元素是值副本,可直接用c.in、c.want访问 - 字段顺序必须与定义严格一致;若需跳过或乱序,可用
in: "xxx"显式赋值 - 匿名结构体无法跨包复用,若需共享,应定义为具名
type testCase struct{...} - 注意:字段名小写(如
in)仅包内可见,在测试中合理,但不可用于 JSON 序列化(因未导出)
最容易被忽略的是:嵌入指针类型(如 *Address)后,初始化时默认是 nil,直接访问字段会 panic;还有同名字段冲突时,Go 不会帮你选,而是直接报 ambiguous selector 错误——必须显式限定。


















