每日一Go-33、Go深入-泛型深度解析--类型推断、约束、any vs T、性能对比、实战模型
***文末有源码下载链接*** 一、为什么需要泛型? 在 Go 1.18 之前,你要么: 泛型的出现,解决三件事: 一句话:Go 泛型是“零成本抽象”的入口。 二、泛型的基本结构 2.1 函数泛型 2.2 类型泛型 2.3 核心语法提醒: 三、类型推断:Go十怎么自动猜T的? 在以下情况自动推断会失效 四、约束 这是最令人模糊的部分。泛型真正的逻辑在于:约束=类型必须满足什么能力 4.1 any:完全不限制 4.2 comparable:允许== != 4.3 Ordered:可用 <>的类型 4.4 ~Type:匹配任何以Type为底层类型的类型 4.5 接口约束:自定义能力模型 五、any vs T:本质区别是什么? any不等于泛型。any是一种约束,意思是:该类型参数没有任何限制。但T仍然是“泛型参数”,不是空接口 后者是泛型,前者不是 六、泛型类型、泛型结构体、泛型方法 6.1 结构体泛型 6.2 方法接收者泛型 七、泛型的性能:与接口和反射对比 7.1 泛型vs接口 泛型性能更好,尤其在高频调用的热路径。 7.2 泛型vs反射 泛型完全碾压反射: 八、泛型常见错误 8.1 ×给泛型方法定义自己的类型参数 8.2 ×误用any any不是万能钥匙,你可能反而失去约束能力 8.3 ×想用泛型实现“运行时类型变化” 泛型是在编译器处理的,不能动态变类型。 九、工程实践:什么时候应该用泛型? 9.1 应该用: 9.2 ×不应该用: 泛型用于抽象“跨类型但逻辑一致”的代码,不适合抽象“行为不同但名字相似”的场景。 十、实战:通用Repository实现 *源码地址* 1、公众号“Codee君”回复“每日一Go”获取源码 2、https://pan.baidu.com/s/1B6pgLWfSgMngVeFfSTcPdg?pwd=jc1s 如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!
func Min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}type Animal[T any] struct {
data []T
}[K V any] x := Min(1, 3) // T = int
y := Min(1.0, 2.0) // T = float64
z := Min("hello", "Codee君") // T = string
xx := Min(1,"2") //× 参数类型必须一致var Cat Animal// ×类型参数不完整
//必须写成
var Cat Animal[string]func Print[T any](x T) {
fmt.Println(x)
}
//等于旧的interface{},但在编译期就是具体类型func IndexOf[T comparable](arr []T, x T) int {
for i, v := range arr {
if v == x {
return i
}
}
return -1
}
arr := []int{1, 2, 3, 4, 5}
fmt.Println(IndexOf(arr, 3))
fmt.Println(IndexOf(arr, 6))
// 输出
//2
//-1// 来自包"golang.org/x/exp/constraints"
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}type MyInt int
// ~int 表示类型约束:T 可以是 int 或任何以 int 为底层类型的自定义类型(如 MyInt)
func Add[T ~int](a, b T) T {
return a + b
}c := Add[MyInt](1, 2)
fmt.Println(c)
//输出
//3type Number interface {
~int | ~float64
}
func Sum[T Number](arr []T) T {
var sum T
for _, v := range arr {
sum += v
}
return sum
}fmt.Println(Sum([]float64{1.1, 2.2, 3.3}))
fmt.Println(Sum([]MyInt{1, 2, 3}))
// 输出
//6.6
//6
func Get(x any) // x 是 interface{}类型
func Get[T any](x T) // x 是具体类型type Animal[T any] struct {
data []T
}func (a *Animal[T]) Append(b T) {
a.data = append(a.data, b)
}
a
[T]
[U any]
x// 数据资源接口
type IRepo[T IModel] interface {
// 分页查询
PageList(c *gin.Context, query *IFilter) (res *response.PageListT[T], err error)
// 分页查询
PageListWithSelectOption(c *gin.Context, query *IFilter, selectOpt []string) (res *response.PageListT[T], err error)
// 查询一个
One(c *gin.Context, id uint) (res T, err error)
// 查询一个
OneWithSelectOption(c *gin.Context, id uint, selectOpt []string) (res T, err error)
// 根据名称查询
OneByName(c *gin.Context, name string) (res T, err error)
// 根据名称查询
OneByNameWithSelectOption(c *gin.Context, name string, selectOpt []string) (res T, err error)
// 添加
Add(c *gin.Context, model T) (newId uint, err error)
// 更新,传什么就更新什么
Update(c *gin.Context, updateFields map[string]any, id uint) (updated bool, err error)
// 删除
Delete(c *gin.Context, id uint) (deleted bool, err error)
}
// 数据资源接口实现
type Repo[T IModel] struct {
DB *gorm.DB
}
// 新建一个数据资源
func NewRepo[T IModel](db *gorm.DB) *Repo[T] {
return &Repo[T]{
DB: db,
}
}
// 分页查询数据
func (r *Repo[T]) PageList(c *gin.Context, f *IFilter) (res *response.PageListT[T], err error) {
db := r.DB
db = (*f).BuildPageListFilter(c, db)
offset := ((*f).GetPage() - 1) * (*f).GetPageSize()
db = db.Model(new(T)).Offset(int(offset)).Limit(int((*f).GetPageSize()))
objs := make([]T, 0)
err = db.Find(&objs).Error
var count int64
db.Offset(-1).Limit(-1).Select("count(id)").Count(&count)
res = &response.PageListT[T]{
List: objs,
Pages: response.MakePages(count, (*f).GetPage(), (*f).GetPageSize()),
}
return
}