go语言之泛型

什么是泛型?

泛型可以理解为在编程过程中,定义的函数、方法时不需要指定类型,类型根据具体的调用参数来指定

1、下面例子定义函数,参数为空接口切片,调用函函数,遍历切片,如图:

执行结果如下:

[root@VM-12-12-centos gong]# go run fan.go 
# command-line-arguments
./fan.go:13:6: cannot use []string{…} (value of type []string) as type []interface{} in argument to fan

从上面看出,执行结果出错,原因是虽然空接口(interface{})可以接受任何类型的数据,但是在和切片和映射配合的时候,无法将[]T转换为[]interface{}也不能将map[string]T转换为map[string]interface{}

使用泛型

1、重新修改上面的例子,声明一个泛型函数,如图:

  • 第6行:在函数名fan和括号中间加了一对中括号[T any],里面参数为T,类型为any,表示支持任意参数,后面的函数参数s的类型则为[]T,这个T就是前面的中括号中的T,表示类型,注意:中括号里的参数不一定为T,写A也行写B也行,但是类型必须写any,否则报错,any为自带关键字
  • 13-15行:调用函数,12、13行中函数名fan后写了一对中括号,括号中为要传递的类型,对应的就是any的值,此中括号可写可不写,15行中就省略了,没有写

执行结果如下:

any可以看做interface{}的别名

2、声明一个泛型切片,如图:

  • 第6行:自定义类型,名字为fans,类型为切片,并使用了泛型
  • 15、18行:实例化变量的时候,需要指定类型,也就是fans后面需要加上[int]才可以

运行结果如下:

3、声明一个泛型通道,如图:

运行结果如下:

4、声明一个泛型map,如图:

  • 第6行:定义map类型变量fans中k的类型为string,v的类型使用泛型any
  • 13、16行:实例化变量fans,指定泛型类型以及map的k和v

运行结果如下:

如果将第6行和第9行中的string也改为any会怎么样?如图:

运行结果如下:

报错内容为缺少可比较约束,为什么呢?因为golang泛型对于map的key有如下约束:

  • key的类型必须是可以比较的,比较运算符为==和!=
  • key的类型不能是function、map、slice(这几个不可比较)
  • 对于interface类型,其动态类型必须可进行比较

可比较的类型有:布尔、整数、浮点、字符串、指针、channel、interface、complex,以及他们组成的结构体、数组。比较是==和!=比较,不涉及大小

不可比较类型有: function、map、slice,以及他们组成的结构体和数组。比较是==和!=比较,不涉及大小

泛型约束

泛型约束指的是约束泛型所能接受的数据类型,更直白的说就是这个 T 可以指代的数据类型

三个特殊的 泛型约束

  • any:任何数据类型
  • comparable:可以经行比较的数据类型(==, ≠)
  • Ordered:可以进行大小判断的数据类型(<, >, ≤, ≥)

1、下面例子使用interface中规定的类型约束,如图:

  • 第8行:定义comp接口,此接口中定义的约束类型只能为int 、string、int16三种
  • 第9行:此种写法表示一个类型集,定义泛型的时候泛型种类必须属于其中某一种
  • 第6行和第13行:使用comp接口中的类型

2、修改第一步中代码,添加自定义类型,调用时候指定自定义类型,如图:

  • 第6行:自定义变量SelfInt,底层的类型为int16
  • 第21行:实例化变量fmap时,传递类型SelfInt

执行结果报错如下:

./fan.go:23:5: SelfInt does not implement comp (possibly missing ~ for int16 in constraint comp)

从上述错误看出,报错原因说是int16类型缺少~,如果不加~,只有int16,那么表示传递的类型必须为int16,但是如果加了~,那么传递的类型可以是int16的所有类型的集合,SelfInt的底层是int16,因此如果要传递SelfInt,需要在int16前面加上~

修改上述代码,将int16改为~int16,再次执行打印结果即可,如图:

注意:建议将int、string这些前面都加上~,以防出错

附加:

1、还可以将上述的类型集分开在多个interface中写,如图:

2、方法不支持泛型,下图中写法就是错误的,如图:

上图中的[T any]写的位置是不对的,因为方法是不支持使用泛型,改造代码如下:

改造后的方法中虽然也有泛型的存在,但是这个泛型依赖的是上面结构体而存在,不是依赖于person()方法存在的,因此改造后的泛型是正确的,执行结果如下:

泛型和接口

type  abc interface {
    hello()string
}
type def interface{
    ~int | ~string
}

官方给出的说明为:

  • 只有方法的接口叫做基本接口(Basic Interface)
  • 包含类型约束的接口叫做一般接口(General Interface)—无论是否包含方法

3、使用泛型自带的comparable进行约束,如图:

上图中调用函数,遍历切片a,遍历的值与传递的变量b进行比较,如果相等则返回切片元素对应的索引,否则返回-1

标签