一、空接口 any(万能类型)
很早就有,any 只是1.18 给的别名,底层完全一样
1. 定义
go
type any = interface{}
any 就是空接口 ,可以存放任意类型:值、结构体、指针、切片、map 全都能塞。
2. 作用
- 脱离具体类型,做通用逻辑
- 用来做类型断言中转
- 泛型底层也依赖空接口思想
3. 用法
go
var a any
a = 123
a = "abc"
a = &config.ServerConfig{}
二、类型断言(核心语法 .(Type))
1. 作用
把 any 万能类型转回真实具体类型,并判断类型是否匹配。
2. 两种写法
写法1:单返回(失败直接panic,不推荐)
go
v := x.(int)
写法2:双返回(安全通用,项目必用)
go
val, ok := x.(目标类型)
// ok=true 断言成功,ok=false 类型不匹配
3. 项目里的实战用法
go
// 临时匿名接口:只要带 Init() 方法就算实现
var initInterface = interface{ Init() }
// 把 &cfg 转成 any,再断言是否实现上面接口
initable, ok := any(&cfg).(initInterface)
if ok {
initable.Init()
}
人话 :判断这个结构体指针有没有Init()方法,有就自动调用。
4. 重点区分
- 断言值类型 :只能调用值接收者方法
- 断言指针类型 :才能调用指针接收者方法
三、匿名临时接口(无定义名接口)
1. 传统接口写法(老教程)
go
type Initer interface {
Init()
}
先定义接口,再结构体实现。
2. 现代极简写法(你现在用的)
不用提前定义接口,直接内联写
go
interface{ Init() }
一行临时生成接口,用完即弃,不用单独建接口文件,配置类首选。
3. Go接口最大特性:隐式实现
没有 implements 关键字
只要结构体拥有接口里全部方法,自动实现该接口,编译器自动识别。
- 有
Init()→ 自动实现interface{Init()} - 无
Init()→ 不实现,断言失败
四、方法接收者:值接收者 VS 指针接收者(重中之重)
1. 值接收者(拷贝操作)
go
func (s ServerConfig) Init() {}
- 调用时拷贝一份全新结构体
- 方法内修改字段不会改动原对象
- 只读场景使用
2. 指针接收者(原地修改)
go
func (s *ServerConfig) Init() {
s.AppKey = "xxx"
}
- 直接操作原结构体内存地址
- 修改永久生效
- 配置初始化、赋值字段 100%用这个
3. 匹配规则(你之前踩坑核心)
- 指针接收者方法 只能由结构体指针调用
- 值类型变量 无法断言匹配指针接收者接口
- 所以
any(&cfg)必须取地址 ,不能用原值any(cfg)
最简总结
改内部字段 → 必用指针接收者
只读数据 → 用值接收者
五、Go 泛型全面精讲(Go1.18+ 新特性)
1. 泛型作用
编写通用函数 ,不绑定固定类型,一套代码支持多种类型。
老Go只能写interface{}做万能类型,繁琐还丢类型安全,泛型完美解决。
2. 基础语法
go
func 函数名[T 类型约束](入参) 返回值 {}
[T any]:T 代表任意类型,any是最大约束T为类型形参,调用时自动推导真实类型
3. 调用方式(类型自动推导)
go
// 直接指定结构体类型,泛型自动实例化
serverCfg, err := config.Load[config.ServerConfig]()
4. 泛型优势
- 一套
Load函数,所有配置结构体通用 - 保留类型安全,不会像空接口那样乱转型
- 抛弃重复写N个配置加载函数
六、指针语义 & 值语义 彻底分清
- 值语义
赋值、传参都会完整拷贝,互不干扰,修改互不影响。 - 指针语义
传递内存地址 ,所有人操作同一个对象,一处修改全局生效。
在配置场景:
- 配置全局单例 → 用指针
- 临时只读副本 → 用值
- 初始化赋值字段 → 必须传指针
七、错误嵌套 %w 新标准写法
老写法
go
return err
丢失原始错误信息,排查困难
新版包装写法(Go1.13+)
go
return fmt.Errorf("加载配置失败: %w", err)
%w嵌套底层原始错误- 保留错误调用链
- 上层可通过
errors.Is/errors.As判断原始错误类型
项目统一错误规范
八、两种 Init 彻底区分(别再混淆)
- 包级 init() 函数(自动执行)
go
package config
func init(){
// 程序导入包自动执行,无需调用
}
全局唯一,包加载自动触发。
- 结构体自定义 Init() 方法(手动/框架触发)
go
func (s *ServerConfig) Init(){}
普通成员方法,不会自动执行,只能通过:
- 手动调用
- 泛型Load内部断言自动调用
九、结构体环境变量标签
go
Code string `env:"SERVER_CODE" envDefault:"hk003"`
env:"变量名":绑定系统环境变量envDefault:"默认值":无环境变量时填充默认值- 仅第三方env库支持,标准库无此标签
十、整套流程完整运行逻辑(从头到尾)
- 调用
Load[ServerConfig]()泛型函数 - 创建空
ServerConfig结构体 env.Parse从环境变量填充字段- 取结构体指针
&cfg转为any - 类型断言判断是否拥有
Init()方法 - 有则自动执行指针接收者Init,拼接生成AppKey
- 返回填充完成+初始化完毕的完整配置实例
- 业务层直接使用,无需手动初始化
最终速记背诵版
- any = 空接口,万能容器
- 类型断言 = any转回真实类型,双返回安全判断
- 匿名接口 = 内联定义方法集合,简洁高效
- 指针接收者 = 改结构体字段必备,必须传地址调用
- 泛型[T any] = 通用万能函数,多类型复用
- %w = 错误嵌套,保留错误链路
- 包init自动跑,结构体Init靠调用