[Golang] 泛型的使用
作者:ShadowYD
https://juejin.cn/post/7194468198270861368
关键字参数
众所周知很多语言的function 中都支持 key=word 关键字参数, 但 golang 是不支持的, 我们可以利用泛型去简单的实现.
func DefaultKeyWordParams[D any](defVal D, params ...D) D {
if len(params) == 0 {
return defVal
}
return params[0]
}
func test(category ...string) {
// 不填写则返回默认值
realCategory := DefaultKeyWordParams[string]("AGroup", category...)
fmt.Println(realCategory)
}
func main () {
test()
}
快速排序
UpdateAt: 2023-02-22
实现一个可进行控制反转的通用类型快速排序, 解决一下原生的sort包进行类型定义的繁琐.
// QuickSort 通用快速排序
func QuickSort[T any](arr []T, compareFn func(a, b T) bool) {
if len(arr) < 2 {
return
}
pivot := arr[0]
left := 1
right := len(arr) - 1
for left <= right {
if compareFn(arr[left], pivot) {
left++
} else if compareFn(pivot, arr[right]) {
right--
} else {
arr[left], arr[right] = arr[right], arr[left]
}
}
arr[0], arr[right] = arr[right], arr[0]
QuickSort(arr[:right], compareFn)
QuickSort(arr[right+1:], compareFn)
}测试用例
func TestQuickSort(t *testing.T) {
nums := []int{9, 3, 1, 7, 4, 8, 6, 2, 5}
fmt.Println("Unsorted:", nums)
QuickSort[int](nums, func(a, b int) bool {
return a < b
})
fmt.Println("Sorted: ", nums)
strs := []string{"orange", "apple", "banana", "kiwi", "grape"}
fmt.Println("Unsorted:", strs)
QuickSort[string](strs, func(a, b string) bool {
return len(a) < len(b)
})
fmt.Println("Sorted: ", strs)
}
去重复
这是一个简单的实现, 复杂点可以通过回调 + 泛型来实现;
func RemoveDuplicate[T string | int | float64](duplicateSlice []T) []T {
set := map[T]interface{}{}
res := []T{}
for _, item := range duplicateSlice {
_, ok := set[item]
if !ok {
res = append(res, item)
set[item] = nil
}
}
return res
}
func main() {
fmt.Println(RemoveDuplicate[string]([]string{"a", "c", "a"}))
fmt.Println(RemoveDuplicate[int]([]int{1, 2, 1, 1, 1}))
}通过控制反转实现通用的去重复方法, 支持任意类型;
type Student struct {
Name string
Age int
}
func NewStudent(name string, age int) *Student {
return &Student{Name: name, Age: age}
}
func DefaultFilter(item interface{}) (uniqueKey interface{}) {
return item.(*Student).Name
}
func RemoveDuplicateWithFilter[T comparable](compareSlice []T, filterFunc func(item interface{}) (key interface{})) []T {
set := map[interface{}]interface{}{}
res := []T{}
for _, item := range compareSlice {
i := filterFunc(item)
_, ok := set[i]
if !ok {
res = append(res, item)
set[i] = nil
}
}
return res
}
func main() {
s := []*Student{
NewStudent("a", 1),
NewStudent("a", 1),
NewStudent("b", 2),
NewStudent("b", 2),
}
l := RemoveDuplicateWithFilter[*Student](s, DefaultFilter)
for _, i := range l {
fmt.Println(i.Name, i.Age)
}
}
联合约束类型
该例子只是一个演示, 没有实际效果 type ID interface {
int | string
}
// 写法 [T ID, D string] == [T int | string, D string]
type UserModel[T ID, D string] struct {
Id T
Name D
}
func NewUserModel[A ID, D string](id A, name D) *UserModel[A, D] {
return &UserModel[A, D]{Id: id, Name: name}
}
func main() {
fmt.Println(NewUserModel[int, string](10, "hello"))
fmt.Println(NewUserModel[string, string]("10", "hello"))
}
分页
这是一段线上在使用的分页代码, 当无法使用外部存储器进行分页时直接使用该对象进行分页, 支持任意类型; type KeepItem bool
// 若需要保留的item 则返回true 即可
type FilterFunc func(item interface{}) KeepItem
type PageList[T any] struct {
Total int `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
List []T `json:"list"`
}
type Pager[T any] struct {
limit int
offset int
total int
pageCnt int
list []T
}
func NewPager[T any](list []T) *Pager[T] {
return &Pager[T]{
limit: 10,
offset: 1,
total: len(list),
list: list,
}
}
func (this *Pager[T]) Filter(filterFn FilterFunc) *Pager[T] {
tmpList := []T{}
for _, item := range this.list {
if filterFn(&item) {
tmpList = append(tmpList, item)
}
}
this.list = tmpList
this.total = len(tmpList)
return this
}
func (this *Pager[T]) Offset(c int) *Pager[T] {
this.offset = c
return this
}
func (this *Pager[T]) Limit(c int) *Pager[T] {
this.limit = c
return this
}
func (this *Pager[T]) List() []T {
// 页码
if this.offset <= 0 {
this.offset = 1
}
// size
if this.limit > this.total {
this.limit = this.total
}
// 总页数
this.pageCnt = int(math.Ceil(float64(this.total) / float64(this.limit)))
if this.offset > this.pageCnt {
return []T{}
}
startIdx := (this.offset - 1) * this.limit
endIdx := startIdx + this.limit
if endIdx > this.total {
endIdx = this.total
}
return this.list[startIdx:endIdx]
}
func (this *Pager[T]) Output() *PageList[T] {
return &PageList[T]{
Total: this.total,
Page: this.offset,
Size: this.limit,
List: this.list,
}
}
// test
func main () {
page := NewPager[int]([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
list := page.Offset(1).Limit(3).Filter(func(item interface{}) KeepItem {
if *item.(*int)%2 == 1 {
return true
}
return false
}).List()
fmt.Println(list)
}
通用初始化模型
可以解决在多态下使用同一个初始化函数进行对象初始化, 写法上有点绕大家自行多实验几次就能明白. type ModelObj interface {
User | Product
}
type User struct {
Uid int
}
func (this *User) SetId(id int) {
this.Uid = id
}
type Product struct {
Pid int
}
func (this *Product) SetId(id int) {
this.Pid = id
}
// TrimModelObj 是一个动态类型的 Interface, 由M决定当前Interface的最终类型
type TrimModelObj[M ModelObj] interface {
*M
SetId(id int)
}
// TrimModelObj[Model] 由第二个参数决定当前的动态类型;
// NewModelObj[*User, User](32) 如 Model 是 User 类型, 最终 TrimModelObj == *User,所以我们需要为 Trim 传递 *User
func NewModelObj[Trim TrimModelObj[Model], Model ModelObj](id int) Trim {
m := new(Model)
t := Trim(m)
fmt.Printf("%p \n", m)
// 类型转换成指定的*Model
t.SetId(id)
return t
}
func main() {
// new user model object
user := NewModelObj[*User, User](32)
fmt.Printf("%p \n", user)
fmt.Printf("%T \n", user)
fmt.Println(user.Uid)
// new product model object
prod := NewModelObj[*Product, Product](18)
fmt.Printf("%p \n", prod)
fmt.Printf("%T \n", prod)
fmt.Println(prod.Pid)
}
微信扫码关注该文公众号作者
戳这里提交新闻线索和高质量文章给我们。
来源: qq
点击查看作者最近其他文章