Golang泛型笔记(一)

golang  

泛型: 把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型

  1. 意义:
  2. 类型的参数化,就是可以把类型像方法的参数那样传递.
  3. 泛型使编译器可以在编译期间对类型进行检测以提高类型安全,减少运行时由于对象类型不匹配引发的异常
  4. 泛型方法,算法的复用
  5. 一句话概括:
  6. 1.写程序的时候,指定泛型后,在程序编译时会进行类型安全检查.
  7. 2.编写代码的时候不需要进行类型转换

泛型语法 使用 中括号[] 而不是 尖括号<> , 原因是尖括号的二义争议,a, b = w < x, y > (z),go 语言支持多返回值,不明确后面是函数还是 表达式返回值

  • 函数泛型格式
  1. func F[T any](p T) { ... }
  • 类型泛型格式
  1. type M[T any] []T
  • 每个类型参数都有类型约束,就像每个普通参数都有一个类型一样
  1. func F[T Constraint](p T) { ... }
  • 接口类型的类型约束
  • 增加 any 预先声明类型约束,允许任何类型的的类型约束

  • 用作类型约束的接口类型可以具有预先声明的类型的列表,只有匹配其中一种类型的类型参数才能满足约束条件.

  • 泛型方法智能使用类型约束所允许的操作

  • 使用泛型函数或类型需要传递类型实参

  • 在一般情况下,类型推断允许省略函数调用的类型参数

Go 语言中的泛型,与其他语言(C++,C#,Java,Rust)的不能混淆,有相似之处,但又不相同.

类型参数

泛型代码是使用我们称之为类型参数的抽象数据类型编写的.运行泛型代码时,类型参数被类型实参替换.

  1. // 不建议的语法
  2. func Print(s []T) { // Just an example, not the suggested syntax.
  3. for _,v := range s {
  4. fmt.Println(v)
  5. }
  6. }
  7. // Print prints the elements of any slice.
  8. // print has a type parameter T and has a single (non-type)
  9. // parameter s which is a slice of that type parameter
  10. func Print[T any](s []T){
  11. // same as above
  12. }
  13. // Call Print with a []int
  14. // Print has a type parameter T, and we want to pass a []int,
  15. // so we pass a type argument of int by writing Print[int]
  16. // The function Print[int] expects a []int as an argument
  17. Print[int]([]int{1,2,3})
  18. // This will print:
  19. // 1
  20. // 2
  21. // 3

约束

  1. // This function is INVALID
  2. func Stringify[T any](s []T) (ret []string) {
  3. for _, v := range s {
  4. ret = append(ret, v.String()) // INVALID
  5. }
  6. return ret
  7. }
  8. // 看上去这个示例没有什么问题,但是 `v` 的类型是 `T`, 而 `T` 是任意类型 且 没有 `String`方法.所以调用 `v.String()`是无效的.
  9. 泛型代码,只能使用其类型参数一只要实现的操作

允许 any 任意类型的操作

  • 声明这些类型的变量
  • 将相同类型的其他值赋值给这些变量
  • 将这些变量传递给函数或者从函数中返回
  • 获取这些变量的地址
  • 将这些类型的值转换或者赋值给接口类型 interface {}
  • 将 T 类型的值转换成 T 类型 (允许但是无用)
  • 使用类型断言将接口类型转换为需要的类型
  • 可以是用 switch 进行类型判断
  • 定义或者使用这些类型的复合类型,例如 这些类型的 slice 切片
  • 将类型传递给一些预声明的函数 例如 new
  • …以后可能还会增加…

定义约束

Go 已经有了定义约束的概念,就是 接口类型.接口类型就是一组方法

  • 调用带有类型参数的泛型函数类似于接口类型变量赋值,类型参数必须实现类型参数的约束.编写泛型函数就想使用接口类型的值:泛型代码只能使用约束允许的操作.
  1. // 在设计中,约束就是接口类型.实现约束就意味着实现接口类型
  2. // Stringer is a type constraint that requires the type argument to have
  3. // a String method and permits the generic function to call String.
  4. // The String method should return a string representation of the value.
  5. type Stringer interface {
  6. String() string
  7. }

any 任意类型约束

约束就是接口类型, 每次在接口定义的时候写 interface{} 会很冗余,所以 any 等价于 interface{},是个预先声明的名称,是 interface{} 的别名或者重新定义的一个类型

  1. // Print prints the elements of any slice.
  2. // Print has a type parmeter T and has a single (non-type)
  3. // parameter s which is a slice of that type parameter
  4. func Print[T interface{}](s []T) {
  5. // same as above
  6. }

约束的使用

对于泛型函数而言,可以将约束视为类型参数的类型

  1. // Stringify calls the string method on each element of s,
  2. // and returns the results
  3. func Stringify[T Stringer](s []T)(ret []string) {
  4. for _, v := range s {
  5. ret = append(ret, v.String())
  6. }
  7. return ret
  8. }
  9. // 单类型参数T 的 约束就是Stringer

多类型参数

  1. // Print2 has two type parameters and two non-type parameters.
  2. func Print2[T1, T2 any](s1 []T1, s2 []T2) {
  3. // ...
  4. }
  5. // 对比
  6. // Print2Same has one type parameter and two non-type parameters.
  7. func Print2Same[T any](s1 []T, s2 []T) {
  8. // ...
  9. }
  10. // Print2 中返回的slice类型可以是不同的类型
  11. // Print2Same 中返回的slice类型必须是相同的类型
  12. // Stringer is a type constraint that requires a string method
  13. // The String method should return a string representation of the value.
  14. type Stringer interface {
  15. String() string
  16. }
  17. // Plusser is type constraint that requires a Plus method
  18. // The Plus method is expected to add the argument to an internal
  19. // string and return the result
  20. type Plusser interface {
  21. Plus(string) string
  22. }
  23. // ConcatTo takes a slice of elements whith a String method and a slice
  24. // of elements with a Plus method. The slices should have the same number of elements
  25. func ConcatTo[S Stringer, P Plusser](s []S, p []P) []string {
  26. r := make([]string,len(s))
  27. for i,v := range s {
  28. r[i] = p[i].Plus(v.String())
  29. }
  30. return r
  31. }
  32. // 单约束可以用于多个类型参数,就像单个约束可以用于多个非类型函数参数一样,约束分别应用与每个类型的参数
  33. // Stringify2 converts two slices of different types to strings,
  34. // and returns the concatenation of all the strings
  35. func Stringify2[T1,T2 Stringer](s1 []T1, s2 []T2) string {
  36. r := ""
  37. for _, v1 := range s1 {
  38. r += v1.String()
  39. }
  40. for _, v2 := range s2 {
  41. r += v2.String()
  42. }
  43. return r
  44. }

泛型类型

  • 定义一个切片,元素类型为任意类型的泛型类型
  1. // Vector is a name for a slice of any element type
  2. type Vector[T any] []T
  3. // 内部使用Vector类型
  4. var v Vector[int] // 等价于使用 type Vector[int] []int
  • 泛型类型可以有方法,方法的接收类型必须声明与接收类型定义中的数量相同的类型参数
  1. // Push adds a value to the end of a vector
  2. func (v *Vector[T]) Push(x T) {
  3. *v = append(*v, x)
  4. }
  • 泛型类型可以引用自身,但是这样做时,类型实参必须是按相同顺序累出的类型形参,此限制可以防止类型实例化的无限递归
  1. // List is a linked list of values of type T.
  2. type List[T any] struct {
  3. next *List[T] // this reference to List[T] is Ok
  4. val T
  5. }
  6. // This type is INVALID.
  7. type P[T1, T2 any] struct {
  8. F *P[T2, T1] // INVALID; must be [T1, T2]
  9. }
  • 实参按相同顺序的限制 适用于直接引用和间接引用
  1. // ListHead is the head of a linked list.
  2. type ListHead[T any] struct {
  3. head *ListElement[T]
  4. }
  5. // ListElement is an element in a linked list with a head
  6. // Each element points back to the head
  7. type ListElement[T any] struct {
  8. next *ListElement[T]
  9. val T
  10. // Using ListHead[T] here is OK.
  11. // ListHead[T] refers to ListElement[T] refres to ListHead[T]
  12. // Using ListHead[int] would not be OK, as ListHead[T]
  13. // would have an indirect reference to ListHead[int]
  14. head *ListHead[T]
  15. }
  • 泛型类型的形参除了有 any 约束外还可以有其他约束
  1. // StringableVector is a slice of some type, where the type
  2. // must have a String method
  3. type StringableVector[T Stringer] []T
  4. func (s StringableVector[T]) String() string {
  5. var sb strings.Builder
  6. for i, v := range s {
  7. if i > 0 {
  8. sb.WriteString(", ")
  9. }
  10. // It's Ok to call v.String herer because v is of type T
  11. // and T's constraint is Stringer
  12. sb.WriteString(v.String())
  13. }
  14. return sb.String()
  15. }


评论 0

发表评论

Top