Go虽无class,但用struct+方法+接口实现面向对象:struct封装数据(小写字段私有),方法绑定到类型(值/指针接收者需统一),嵌入替代继承,接口隐式实现且小而专。

Go 不是传统意义上的面向对象语言,但它能完成所有你真正需要的面向对象任务——只是换了一种更直接、更少抽象的方式。关键不是“有没有类”,而是“怎么组织数据和行为”。
struct 是你的“类”,但只管数据
Go 里没有 class,只有 struct。它只负责打包字段,不带逻辑:
type User struct {
Name string
Email string
age int // 小写 → 包内私有
}
注意:age 字段首字母小写,外部包无法访问,这是 Go 唯一的封装机制;没有 private 关键字,也不允许外部直接读写。
- 结构体实例化推荐用字段名初始化:
u := User{Name: "Alice", Email: "a@example.com"} - 避免用
new(User)后逐个赋值,易漏字段且可读性差 - 不要试图给
int或[]string直接加方法——必须先用type定义新类型,比如type UserID int
方法绑定到类型,不是 struct
方法不属于 struct,而是属于某个具体类型(可以是 User,也可以是 UserID)。接收者决定行为语义:
func (u User) DisplayName() string { return u.Name } // 值接收者:安全,不可改原值
func (u *User) SetEmail(e string) { u.Email = e } // 指针接收者:可修改原值
常见错误:User 类型同时混用值接收者和指针接收者方法,会导致接口实现不一致或调用 panic。
- 如果结构体较大(比如含 slice、map、大数组),优先用指针接收者,避免拷贝开销
- 只要方法要修改字段,就必须用指针接收者;否则两者都可,但建议统一风格
- 调用时
u.SetEmail("x")和(&u).SetEmail("x")都合法——Go 自动取地址,但别依赖这个隐式转换
组合代替继承,嵌入是语法糖
Go 没有 extends,但支持结构体内嵌(anonymous field)来复用字段和方法:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
type Admin struct {
User // 内嵌 → 提升 User 的导出字段和方法
Privileges []string
}
这时 admin.DisplayName() 可以直接调,但要注意:
- 内嵌不是继承:
Admin不是User的子类,不能向上转型 - 字段冲突时(比如两个内嵌类型都有
Name),必须显式限定:a.User.Name - 方法提升只对导出方法有效;
User的私有方法不会被提升
接口才是多态核心,而且是隐式的
Go 的接口是契约,不声明“实现”,只看有没有对应方法:
type Notifier interface {
Notify() error
}
func send(n Notifier) { n.Notify() } // 任意类型,只要实现了 Notify 就能传进来
你不需要在 User 定义里写 implements Notifier ——只要它有 func (u *User) Notify() error,就自动满足接口。
- 小接口优先:
io.Reader只有一个Read方法,比大而全的接口更易实现、更灵活 - 接口定义尽量放在调用方包里(比如
service包定义UserRepository),而非实现方(db包),降低耦合 - 空接口
interface{}能存任何值,但失去类型信息;用前务必做类型断言或switch v := x.(type)
最常被忽略的一点:接口变量本身是 (type, value) 二元组。如果把 nil *User 赋给接口,接口不为 nil ——这导致大量 “nil pointer dereference” 隐藏 bug。检查接口是否为空,得先判底层值,不能只看接口变量 == nil。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










