• 初探泛型


    一、前言

    go1.18版本已经发布有几天了, 随着1.18的发布,大家呼吁已久的泛型也与大家正式见面了。趁着这节假日之际,学习整理范型的用法。

    在我们以往中,如果要声明一个带参数的函数,这个函数的参数类型可以是任意类型,我们要怎么做呢?我们肯定想到的是interface{}

    在go中,any表示泛型,那么泛型底层是怎么做的呢?在builtin/builtin.go文件中,我们可以看到type any = interface{}的声明,原来它底层也是interface{}。

    二、常规操作

    1、泛型切片

    下面我们定义一个底层类型为切类型的新类型,它是可以存储任意类型的切片。

    // 定义泛型切片vector
    type vector[T any] []T   // [T any]参数的类型
    
    func main() {
    	v1 := vector[int]{1, 2, 3, 4, 5}
    	fmt.Println(v1) // [1 2 3 4 5]
    	v2 := vector[string]{"李志", "谢天笑", "老狼"}
    	fmt.Println(v2) // [李志 谢天笑 老狼]
    	v3 := vector[float64]{2.22, 3.33, 4.44}
    	fmt.Println(v3) // [2.22 3.33 4.44]
    }
    

    2、泛型map

    type M[K string, V any] map[K]V // 这里的K不支持any,由于map底层是hash
    
    func main() {
    	m1 := M[string, int]{"number": 21}
    	m1["number"] = 33
    	fmt.Println(m1)
    	m2 := M[string, string]{"name": "李志"}
    	m2["key"] = "老狼"
    	fmt.Println(m2)
    }
    

    3、声明一个泛型chan

    type C[T any] chan T
    
    func main() {
    	c1 := make(C[int], 2)
    	c1 <- 10
    	c1 <- 20
    	close(c1)
    	for c := range c1 {
    		fmt.Println(c)
    	}
    
    	c2 := make(C[string], 2)
    	c2 <- "李志"
    	c2 <- "老狼"
    	close(c2)
    	for c := range c2 {
    		fmt.Println(c)
    	}
    }
    

    4、声明一个泛型函数

    func foo[T any](s T) {
    	fmt.Println(s)
    }
    
    func main() {
    	foo[string]("李志")  // 显著指定参数的类型
    	foo(123)
    	foo([3]string{"朱格乐", "阿玛尼", "张怡然"})
      
      // 这个也是显著指定类型,也可以不指定,指定了个人感觉看着好烦琐。。。
    	foo[[]string]([]string{"李志", "谢天笑", "木马", "张玮玮"}) 
    
    	foo(map[string]any{
    		"name":    "李志",
    		"age":     43,
    		"address": "南京",
    	})
    }
    

    三、范型约束

    1、使用interface中规定的类型约束范型函数的参数

    type NumStr interface {
    	Num | Str
    }
    
    type Num interface {
    	int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | complex64 | complex128
    }
    
    type Str interface {
    	string
    }
    
    func add[T NumStr](a, b T) T {
    	return a + b
    }
    
    func main() {
    	fmt.Println(add(3, 4))
    	fmt.Println("hello", "world")
    }
    

    说明:NumStr是我们新增的类型列表表达式,它是对类型参数进行约束的,使用|表示取并集。如果传入的参数不在集合限制范围内,则会抛出错误,另外,类型不能混用。

    2、使用interface中的规定的方法来约束泛型的参数

    type Num1 int
    
    func (n1 Num1) String() string {
    	return strconv.Itoa(int(n1))
    }
    
    type Num2 string
    
    func (n2 Num2) String() string {
    	return string(n2)
    }
    
    type ShowNum interface {
    	String() string
    	~int | ~string
    }
    
    func ShowNumList[T ShowNum](s []T) (result []string) {
    	for _, v := range s {
    		result = append(result, v.String())
    	}
    	return
    }
    
    func main() {
    	fmt.Println(ShowNumList([]Num1{1, 2, 3, 4, 5}))
    	fmt.Println(ShowNumList([]Num2{"1", "2", "3", "4", "5"}))
    }
    

    3、使用comparable约束

    comparable是由所有可比较类型实现的接口(布尔、数字、字符串、指针、通道、类似类型的数组,字段均为可比较类型的结构)。可比接口只能用作类型参数约束,不是作为变量的类型。

    func foo[T comparable](a []T, b T) int {
    	for i, v := range a {
    		if v == b {
    			return i
    		}
    	}
    	return 0
    }
    
    func main() {
    	fmt.Println(foo([]int{1, 2, 3, 4, 5, 6}, 2))
    	fmt.Println(foo([]string{"李大鹅", "李志"}, "李志"))
    }
    
  • 相关阅读:
    关于组建“智彩足球技术研究团队”的说明
    2次成功投诉EMS和中国移动的经验
    为什么选择玩足球彩票以及玩彩票的心态?
    【原创】机器学习之PageRank算法应用与C#实现(1)算法介绍
    【原创】开源Math.NET基础数学类库使用(17)C#计算矩阵条件数
    【原创】开源Math.NET基础数学类库使用(16)C#计算矩阵秩
    【文章】本站收集与转载文章目录
    【原创】.NET读写Excel工具Spire.Xls使用(3)单元格控制
    分享一个Visual Studio的背景插件,让堆码更富情趣
    【原创】开源Math.NET基础数学类库使用(15)C#计算矩阵行列式
  • 原文地址:https://www.cnblogs.com/huiyichanmian/p/16100859.html
Copyright © 2020-2023  润新知