代码复用,也叫模板
函数和类型(含接口)支持类型参数,方法不支持
通常以单个大写字母命名类型参数(类型参数也是go1.18才引入的,给Go带来了泛型编程的能力,之前需要使用interface编写重复的代码)
类型参数必须有约束,可以定义一个只接受实现了某个接口的类型参数的泛型函数
type Addable[T any] interface { Add(T) T } func Sum[T Addable[T]](a, b T) T { return a.Add(b) }
Sum
函数的类型参数 T
被约束为必须实现了 Addable
接口
雨痕大佬的代码
package main import ( "cmp" ) func maxValue[T cmp.Ordered](x, y T) T { if x > y { return x } return y } func main() { println(maxValue[int](1, 2)) // 实例化,类型实参。 println(maxValue(1.1, 1.2)) // 类型推导,省略。 //泛型可以进行类型推导,省略实例化 }
type N struct{} func (N) String() string { return "N" } func test[T fmt.Stringer](v T) { fmt.Println(v) } func main() { // test(1) // int does not implement fmt.Stringer (missing method String) test(N{}) }
类型集合
接口:普通接口(能做什么)
类型约束(谁来做)(指定实现接口的类型集合
type Integer interface { Signed | Unsigned } type Signed interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 }
|:匹配任一类型即可
~:底层类型(underlying type)是该类型的所有类型(断言进行判断)
泛型的实现
通常为
模板:每次调用生成代码实例,(参数类型相同
package main import ( "os" "text/template" ) type Person struct { Name string Age int } func main() { t := template.Must(template.New("person").Parse("Name: {{.Name}}, Age: {{.Age}}\n")) p := Person{Name: "John", Age: 30} t.Execute(os.Stdout, p) }
字典:
package main import "fmt" func main() { m := make(map[string]int) m["Alice"] = 23 m["Bob"] = 34 fmt.Println("Map:", m) }
golang的实现介于两者之间,GCShape stenciling with Dictionaries,使用字典进行 GCShape 模板制作
任意指针类型,或具有相同底层类型,属于同一GCShape组
8.2性能
指针版本会逃逸到堆上
type Tester interface { *A | *B test() } func test[T Tester](a T) { a.test() } // ----------------------------- func main() { var a A = 1 var b B = 2 test(&a) test(&b) }