泛型
没有泛型时,用interface
模拟泛型,需要写很多类型断言。而且用interface
,由于编译期间不知道它是什么类型,会导致变量逃逸到堆上,加大了 GC 的压力。
泛型的原理是编译器为支持的类型自动生成对应的函数代码,比如定义了func Get[K any](k K)
,调用代码是Get(1)
和Get("test")
,那么编译器会自动生成func Get(k int)
和func Get(k string)
,调用的是自动编译器自动生成的函数,而泛型定义的函数只是一个用于生成代码的模板。
Go 1.18 正式支持了泛型,Tutorial。
泛型 vs interface
对于同一个泛型map、slice、channel变量,只能存储相同的类型。如上[]Price{1, "2"}
这是不可以的,而如果用interface
是可以的。
所以说,泛型的本质是编译阶段的代码生成,它并不会替代interface
,interface
主要用来实现语言的动态特性,与泛型的适用场景不同。那么泛型适用什么场景呢?
package atomic
func AddInt32(addr *int32, delta int32) (new int32)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint64(addr *uint64, delta uint64) (new uint64)
如上atomic
标准库中这几个方法,大体逻辑完全相同,仅参数类型不同,没有泛型就得把相似功能的函数都实现一遍,需要写很多重复的代码,复用性不好。
使用interface中规定的方法和类型来双重约束泛型的参数
package main
import (
"fmt"
"strconv"
)
type Price int
type ShowPrice interface {
String() string
~int | ~string // 如果不加~,编译是不通过的。~int表示底层类型为int的都满足约束条件
}
func (i Price) String() string {
return strconv.Itoa(int(i))
}
func ShowPriceList[T ShowPrice](s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return
}
func main() {
fmt.Println(ShowPriceList([]Price{1, 2}))
}
map,slice,channel,struct的泛型支持
type Number interface {
int | int32 | int64 | float64 | float32
}
type KvMap[K comparable, V Number] map[K]V
func (kv KvMap[K, V]) Set(k K, v V) KvMap[K, V] {
kv[k] = v
return kv
}
type Slice[V Number] []V
func (s Slice[V]) Append(v V) Slice[V] {
s = append(s, v)
return s
}
type CH[V any] chan V
type Kv[Vt Number] struct {
K string
V Vt
}
func main() {
var kvm1 = make(KvMap[int, float32])
kvm1[10] = 20.0
fmt.Printf("kvm1=%v \n", kvm1)
var s1 = make(Slice[float64], 2)
kvm1[1] = 30.0
fmt.Printf("s1=%v \n", s1)
var s2 = make(Slice[int64], 1)
s2 = s2.Append(2)
fmt.Printf("s2=%v \n", s2)
ch1 := make(CH[int], 10)
ch1 <- 66
cc := <-ch1
fmt.Println(cc)
var kv1 = Kv[int]{K: "kv1", V: 1}
fmt.Printf("kv1=%v \n", kv1)
}
注:结构体方法内部仅可以使用定义在这个结构体对象上的泛型,如果想同时使用多种,需要在定义那里都写上,用,
分割。
Last updated
Was this helpful?