泛型
先看一下这段代码。
go
package main
import "fmt"
func main() {
strs := []string{"a", "b"}
printArray(strs)
}
func printArray(arr []interface{}) {
for _, a := range arr {
fmt.Println(a)
}
}
上面的代码中,我们想要打印参数arr的信息。运行报错
go
cannot use strs (variable of type []string) as []interface{} value in argument to printArray
想要解决的话,按照之前我们的学习,可以将函数改编如下(使用断言)
go
package main
import "fmt"
func main() {
strs := []string{"a", "b"}
printStringArray(strs)
}
func printStringArray(arr interface{}) {
for _, a := range arr.([]string) {
fmt.Println(a)
}
}
但这样会有一个坏处,当我们想要打印另一个非string的数组时,就不得不再写一个方法
go
package main
import "fmt"
func main() {
ints := []int{1, 2}
printIntArray(ints)
}
func printIntArray(arr interface{}) {
for _, a := range arr.([]int) {
fmt.Println(a)
}
}
这样处理,就会导致有无限多相似的代码产生,这样的代码时不合格的。
此时,泛型就出现了。它的意义时不在方法定义时决定变量的类型,而让使用者使用时决定。
go
package main
import "fmt"
func main() {
ints := []int{1, 2}
strs := []string{"a", "b"}
printArray(ints)
printArray(strs)
}
func printArray[T string | int](arr []T) {
for _, a := range arr {
fmt.Println(a)
}
}
T代表了用户传入的类型,并对T进行了约束。上面的代码中,我们再定一个float的数组,是无法通过程序校验的,因为我们约束了T的可用类型为stirng与int。
使用泛型,你可能会产生一个疑惑,通过我们刚刚学习的反射,再加上接口。也可以类似泛型这样的函数。这样是可行的,但反射的机制存在一些问题
- 1.用起来麻烦
- 2.失去了编译时的类型检查,容易出错
- 3.性能不理想
结论:当需要因为不同类型写完全相同的逻辑代码时,使用泛型时最合适的选择。
泛型类型
- 泛型切片
go
package main
import "fmt"
func main() {
type Slice[T int | string | float32] []T
var a Slice[int] = []int{1, 2, 3}
var b Slice[string] = []string{"a", "b", "c"}
var c Slice[float32] = []float32{1, 2, 3}
fmt.Printf("%T", a)
fmt.Println(a)
fmt.Printf("%T", b)
fmt.Println(b)
fmt.Printf("%T", c)
fmt.Println(c)
}
//main.Slice[int][1 2 3]
//main.Slice[string][a b c]
//main.Slice[float32][1 2 3]
- 泛型map
go
package main
import "fmt"
func main() {
type MyMap[KEY int | string, VALUE float32] map[KEY]VALUE
var m1 MyMap[string, float32] = map[string]float32{
"a": 1.1,
"b": 1.2,
}
fmt.Println(m1)
}
- 其他
go
//泛型结构体
type MyStruct[T int | string] struct {
id T
Name stirng
}
//泛型接口
type IPrintData[T int | float32 | string] interface {
}
//泛型通道
type MyChan[T string | int] chan T
泛型函数与方法
go
package main
import "fmt"
func main() {
//给泛型添加方法
var s MySlice[int] = []int{1, 2, 3, 4}
fmt.Println(s.Sum())
var s1 MySlice[float64] = []float64{1.1, 2.1, 3.1, 4.1}
fmt.Println(s1.Sum())
//泛型函数
fmt.Println(Add[int](1, 2))
fmt.Println(Add[string]("1", "2"))
//如果类型能被自动推断,函数调用时的T可以省略
fmt.Println(Add(1, 2))
fmt.Println(Add("1", "2"))
}
type MySlice[T int | string | float64] []T
func (s MySlice[T]) Sum() T {
var sum T
for _, v := range s {
sum += v
}
return sum
}
func Add[T int | float32 | string](a T, b T) T {
return a + b
}
实际开发中,泛型使用较多的场景就是泛型的函数与方法。
自定义泛型约束
go
package main
import "fmt"
func main() {
fmt.Println(GetMaxNum(1, 2))
fmt.Println(GetMaxNum(1.5, 2.6))
}
type MyInt interface {
int | int8 | int16 | int32 | float64
}
func GetMaxNum[T MyInt](a, b T) T {
if a > b {
return a
}
return b
}