前言
泛型在很多面向对象的编程中大多是有支持的,比如c++,java,python。泛型可以解决相同实现但是类型不同导致编码重复的问题,在Go的早期版本是不支持泛型的,这也是被很多人拿来吐槽Go的一个点。早期Go中可以使用接口类型加上断言或者反射来实现泛型编程,使用反射又会拉低Go的执行效率。所以Go在1.18版本加入了对泛型的支持,来优化使用反射比较麻烦,编译时检查不出错误,性能较差。泛型可以减少重复代码增加安全性,针对不通类型书写同样的逻辑可以使用泛型简化代码的问题。
Go泛型特点和语法
定义泛型类型
如声明一个泛型切片类型:
func main() {
type MySlice[T int | string] []T
is := MySlice[int]{1, 2, 3}
fmt.Println(is)
}
定义map泛型类型:
type MyMap[KEY int | string, VALUE string] map[KEY]VALUE
m := MyMap[int, string]{1: "v1"}
fmt.Printf("type:%T,m:%+v", m, m)
定义泛型结构体:
type MyStruct[T int | string] struct {
id T
name string
}
ms := MyStruct[string]{
id: "1",
name: "redhat",
}
fmt.Printf("type:%T,m:%+v\n", ms, ms)
定义泛型接口:
package main
import "fmt"
type printer[T any] interface {
print(T)
}
type stringPrinter struct{}
func (s stringPrinter) print(v string) {
fmt.Println(v)
}
func print[T any](sp printer[T], v T) {
sp.print(v)
}
func main() {
sp := stringPrinter{}
print(sp, "nihao")
}
定义泛型管道:
package main
import (
"fmt"
"time"
)
type myChan[T any] chan T
func testChan[T any](c myChan[T]){
for k := range c {
fmt.Println(k)
}
}
func main() {
c := make(myChan[string])
go testChan(c)
c <- "1"
c <- "2"
a := make(myChan[int])
go testChan(a)
a <- 1
a <- 2
time.Sleep(time.Duration(2) * time.Second)
}
定义泛型接收者
package main
import (
"fmt"
)
type MySlice[T int | string] []T
func (s MySlice[T])sum() T{
sum:=T(0)
for _,v:=range s{
sum+=v
}
return sum
}
func main() {
is := MySlice[int]{1, 2, 3}
fmt.Println(is.sum())
ic := MySlice[string]{"1", "2", "3"}
fmt.Println(ic.sum())
}
接收者这里可以直接使用T。
定义限制泛型类型
go原生提供了两个限制类型,一个是any一个是comparable,也可以自定义限制类型:
package main
import (
"fmt"
)
type mygeric interface{
int | string
}
type MySlice[T mygeric] []T
func (s MySlice[T])sum() T{
sum:=T(0)
for _,v:=range s{
sum+=v
}
return sum
}
func main() {
is := MySlice[int]{1, 2, 3}
fmt.Println(is.sum())
ic := MySlice[string]{"1", "2", "3"}
fmt.Println(ic.sum())
}
使用泛型实现set
package main
import "fmt"
type Set[T comparable] map[T]struct{}
func (s *Set[T]) Set(key T) {
(*s)[key] = struct{}{} //这里注意解引用
}
func (s *Set[T]) Lens() int {
return len(*s)
}
func (s *Set[T]) Range() []T {
var nums []T
for key := range *s {
nums = append(nums, key)
}
return nums
}
func (s *Set[T]) IsInside(key T) bool {
_, ok := (*s)[key]
return ok
}
func (s *Set[T]) Delete(key T) {
delete(*s, key)
}
func CreateSet[T comparable]() *Set[T] {
m := make(Set[T])
return &m //这里使用new的话会返回空map,不可以直接写操作
}
func main() {
m := CreateSet[int]() //这里注意函数调用
m.Set(1)
m.Set(2)
fmt.Println(m.Lens())
fmt.Println(m.Range())
fmt.Println(m.IsInside(1))
m.Delete(1)
fmt.Println(m.IsInside(1))
}
总结
泛型,类比形式参数,其实泛型就是形式类型,函数中使用形式参数传参,调用时使用实参。类型也是,定义时为形式类型,实际使用时要指定实际类型。类型形参和类型实参概念。
--EOF
版权属于:redhat
本文链接:https://blog.zhangshuocauc.cn/archives/22/
本站采用“知识共享署名 - 非商业性使用 - 相同方式共享 4.0 中国大陆许可协议” 进行许可 您可以转载本站的技术类文章,转载时请以超链接形式标明文章原始出处,Thank you.