golang从1.18加入了泛型,采取的是[ ]这个符号,仅供自己学习使用,示例代码之中会大量使用腾讯云开发那篇文章的例子
一、泛型的简单使用
包括在函数、方法和结构体等中如何添加泛型
1.函数上泛型的使用
直接框定T的类型
// 定义
func add1[T int | float32 | float64](a, b T) T {
c := a + b
return c
}
// 调用,两种调用方式都可以,也就是说golang会推到出泛型的类型
add1(1,2)
add1[int](1,2)
这样写泛型的定义可能过长,因此可以使用使用一个type去定义一下泛型的具体内容,这个interface的用法明显不同于传统的用法,我们后面会讲到
type mixType interface {
float32 | int | float64
}
func add[T mixType](a, b T) T {
c := a + b
return c
}
当然可以定义多个泛型,比如K/V/Z之类的,这个就不写了
2.结构体之中的泛型
// 定义
type Struct1 [T string|int|float64] struct {
Title string
Content T
}
// 实例化
test:= Struct1[string]{
Title:"aaa"
Content :"bbb"
}
// 带泛型的结构体的实例化没有类型推导,也就是说必须要具体给出类型
除此之外还有一个匿名结构体的问题,golang之中的结构体可以直接定义并初始化,如下:
test := struct{
Name string
}{
Name: "huangfeng",
}
这种方式不能带上泛型,也就是如下的写法是没有的
test := struct[T string|int]{
Name T
}[string]{
Name: "huangfeng",
}
编辑器会在struct关键词下报错:expected '{', found '[',struct后面不能跟“[”
3.type自定义类型
和ts一样,type关键词可以自定义类型并且还可以在类型上挂载对应的方法
//申明一个泛型切片
type Slice1 [T int|float64|string] []T
//实例化,并赋值
var MySlice Slice1[int] = []int{1,2,3}
//这里的泛型类型不能自动推导,也需要明确指定出来
4.map中的泛型
map的具体类型我们一般是在make函数之中进行确定,在使用泛型之后,我们可以借助type自定义类型去确定泛型
// 定义
type MyMap[K string | int, V string | int] map[K]V
// 实例化
test := MyMap[string, int]{
"1": 1,
"2": 2,
}
fmt.Println(test["1"])
这样的实例化方法还是蛮新潮的,感觉都有点不像golang
5.chan中的泛型
type gchan[T any] chan T
实例化
strchan := make(gchan[string])
还是借助了type自定义类型的方法
6.interface中的泛型
我们后面会提到一种新类型的interface,这里的interface指的我们传统意义上的接口
package main
import "fmt"
type MyInterface[T int | string] interface {
WriteOne(data T) T
ReadOne() T
}
type Note struct {
}
func (n Note) WriteOne(one string) string {
return "hello"
}
func (n Note) ReadOne() string {
return "small"
}
// 实例化
func main() {
var one MyInterface[string] = Note{}
fmt.Println(one.WriteOne("hello"))
fmt.Println(one.ReadOne())
}
这边的方法的调用是不能直接指定泛型的,它的泛型由接口去控制,看上去方法上的泛型应该比较复杂
7.方法上的泛型
这一部分是腾讯那篇文章上提到的问题,在方法上不能主动指定泛型,这句话可能比较难以理解,但是写出来就懂了
package main
import "fmt"
type Person[T int | string] struct {
Name T
Age T
}
func (p Person[T]) SayName() T {
return p.Name
}
//不支持的写法
func (p Person[T]) SayName[V](age V) T {
return p.Name
}
// 实例化
func main() {
test := Person[string]{
Name: "huanfeng",
Age: "12",
}
fmt.Println(test.SayName())
}
这里的SayName作为方法是不能像函数一样在后面再次指定泛型的,我想这是因为方法的泛型应该由interface的泛型和机构体的泛型相影响,个人认为这是一个比较合理的解释。
二、一些泛型上的细节
1.any类型和调用
any类型本身就是空接口的别名
func add2[T any](a T) {
fmt.Printf("a的类型是%T\n", a)
}
add2(float64(1)) //any类型会根据传入的参数来进行改变
func add3[T any]() T {
var a T
return a
}
fmt.Printf("add3结果的类型是%T\n", add3[float64]())
//float64 现在这种没有参数的情况就需要指定泛型的类型
但是any类型不是万能的,比如有些操作只能由部分类型进行
func Sum[T any] (a, b T) T {
return a+b
}
//报错:
invalid operation: operator + not defined on a (variable of type T constrained by any)
这里的话两个any类型之间不一定能进行加的操作
2.关于comparable
func compare[T any](arg []T, v T) int {
for i, num := range arg {
if num == v {
return i
}
}
return -1
}
这样的写法是不成立的,因为golang无法识别num和v是否是可以比较的类型
func compare[T comparable](arg []T, v T) int {
for i, num := range arg {
if num == v {
return i
}
}
return -1
}
定义为comparable就是告诉哦golang这个泛型是可以比较的
重点说一下哪些类型是可以比较,哪些是不可以比较
1.这些类型是可以比较的:布尔,整数,浮点,complex,字符串,指针,channel,
interface,可比较类型组成的结构体,可比较类型组成的数组
2.此外,func map和slice是不可比较的,此外以上三者类型组成的结果体和数组也是不可比较的
3.这些比较是==和!=的比较,不涉及大小
3.Orderd
这个类型就涉及到大小比较的例子,比如我们的int和字符串等等,但是这个类型是需要从其他包中引入的,下面是腾讯的例子
//导入constraints包
import (
"fmt"
"golang.org/x/exp/constraints"
)
//T的约束类型是:constraints.Ordered
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
} else {
return b
}
}
三、新的interface
1.最后再说一说1.18之后interface的变化
原先的interface就是方法的集合(基本接口),看到上面应该知道了还多了一种混合类型的功能(一般接口,我有点搞),看一下例子
下面这个是基本接口,只有方法
type error interface {
Error() string
}
下面这个是一般接口
type MyError interface {
int | float64
Error() string
}
只要带类型,不管有没有方法都叫一般接口
对于下面那个带类型又带接口的泛型,是不能实例化的,至少是现在没有找到实例化的方法(腾讯云那篇文章里是这样的说的,我也没找到对应的方法)
2.一般接口的使用
此处讨论的一般接口指的是没有方法的接口,我们从上面的实践也可以看出了,它是一个取并集的操作,如下
package main
type MyNumber interface {
int | float32
}
func Add[T MyNumber](a, b T) T {
return a + b
}
// 实例化
func main() {
Add[int](1, 2)
Add[float32](1, 2)
}
当然我们还有一个取交集的操作
package main
type MyNumber interface {
int | float32
}
type MyNumber1 interface {
int | float64
}
type MyNumber3 interface {
MyNumber
MyNumber1
}
func Add[T MyNumber3](a, b T) T {
return a + b
}
// 实例化
func main() {
Add[int](1, 2)
}
MyNumber3这个类型就只能是int了,如果取交集的类型之中没有任何兼容的部分,那么这个泛型传什么都不行
3.底层约束类型
package main
type MyNumber interface {
~int | ~float32
}
type MyInt int
func Add[T MyNumber](a, b T) T {
return a + b
}
// 实例化
func main() {
Add[MyInt](1, 2)
}
在这个MyNumber之中,我们在基础类型前面加上了~,这个蝌蚪尾巴表示只要是底层类型是int或者float32的都可以,比如这里我创建的这个MyInt
4.单一递归继承
抄腾讯的,如下
type Slice1[T bool | float64 | string | int] []T
type Slice2[T bool | float64 | string] Slice1[T]
type Slice3[T bool | int] Slice2[T] 该行出现报错
因为它是单一递归继承的,只会检查它的上一级的取值范围是否覆盖,也就是Slice2类型中有没有int