泛型

没有泛型时,用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是可以的。

所以说,泛型的本质是编译阶段的代码生成,它并不会替代interfaceinterface主要用来实现语言的动态特性,与泛型的适用场景不同。那么泛型适用什么场景呢?

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?