1 Star 0 Fork 0

tking / micro-service

Create your Gitee Account
Explore and code with more than 8 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Please pay attention to the specific project description and its upstream code dependency when using it.
Clone or Download
go泛型入门.md 5.38 KB
Copy Edit Web IDE Raw Blame History
tking authored 2022-03-27 09:05 . go泛型

go泛型入门

2022年3月15日,go团队发布了Go 1.18,这个版本是个大型版本,其中包括新功能泛型的支持。 本篇学习记录Go 中泛型的基础知识

  • 类型参数
  • 约束
  • 声明类型约束
  • 调用过程
  • 泛型类型

我们从一个math求和例子开始:

package main

import "fmt"

type Number interface {
	int64 | float64
}

func main() {
	// Initialize a map for the integer values
	ints := map[string]int64{
		"first": 34,
		"second": 12,
	}
	// Initialize a map for the float values
	floats := map[string]float64{
		"first": 35.98,
		"second": 26.99,
	}
	fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
		SumIntsOrFloats(ints),
		SumIntsOrFloats(floats))
}

// SumIntsOrFloats sums the values of map m. It supports both floats and integers
// as map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
	s += v
	}
	return s
}

执行代码,输出:

Generic Sums, type parameters inferred: 46 and 62.97

类型参数(type parameter)

func GenericFoo[P aConstraint, Q anotherConstraint](x,y P, z Q)
  • 类型参数在函数名与函数参数列表之间,通过一个方括号括起
  • P,Q 是类型形参的名字,也就是类型,通常都是用单个大写字母命名
  • aConstraint,anotherConstraint 代表类型参数的约束(constraint),我们可以理解为对类型参数可选值的一种限定。

类型具化(instantiation) (调用过程)

泛型函数调用分两个阶段

  1. 具化(instantiation)
  2. 调用(invocation)

举个栗子:

 package main

 import (
	"fmt"
	"golang.org/x/exp/constraints"
)

func main() {
	x, y := 2, 3
	min := GMin[int](x, y)
	fmt.Printf("Int-Generic GMin: %v and %v min = %v", x, y, min)
}

func GMin[T constraints.Ordered](x, y T) T {
	if x < y {
		return x
	}
	return y
}

运行代码,输出

Int-Generic GMin: 2 and 3 min = 2

GMin调用过程伪代码 :

fmin := GMin[float64]
m := fmin(2.71, 3.14)

接着我们来看一下 constraint

约束(constraint)

约束(constraint)规定了一个类型实参(type argument)必须满足的条件要求。如果某个类型满足了某个约束规定的所有条件要求,那么它就是这个约束修饰的类型形参的一个合法的类型实参

还是以上面的例子,跳转constraints pkg,可以看到 Ordered 是一个interface

type Ordered interface {
	Integer | Float | ~string
}

其中

  1. ~ ( 泛类型)的用途: 对于类型约束,我们通常不关心特定类型,例如string; 我们对所有字符串类型都感兴趣。表达式~string表示基础类型为 的所有类型的集合string。这包括类型string本身以及所有使用定义声明的类型,例如type MyString string.
  2. | 分隔类型列表中的多个类型实参类型

回到刚才的例子中,说明 x,y 只要是 Integer | Float | ~string 中的一种即符合要求。

假设我们使用了一个不正确(不能排序的类型)

x, y := true false
min := GMin[bool](x, y)

执行代码,输出错误

# tkingo.vip/egs/generics-demo
.\m.go:17:14: bool does not implement constraints.Ordered

约束包 constraint-pkg

// Package constraints defines a set of useful constraints 
to be used// with type parameters.
package constraints

声明类型约束

将把之前定义的约束移到它自己的接口中,以便您可以在多个地方重用它。以这种方式声明约束有助于简化代码,例如当约束更复杂时。 约束接口也可以引用特定类型。


type Number interface {
    int64 | float64
}

还是上面的例子,我们改成自定义的 Number

package main

import (
	"fmt" 
)

func GMin[T Number](x, y T) T {
	if x < y {
		return x
	}
	return y
}

type Number interface {
	int | int64 | float64
}

func main() {
	x, y := 2, 3
	min := GMin[int](x, y)
	fmt.Printf("Int-Generic GMin: %v and %v min = %v", x, y, min)
}

执行代码,输出:

Int-Generic GMin: 2 and 3 min = 2

泛型类型

除了函数可以携带类型参数变身为“泛型函数”外,类型也可以拥有类型参数而化身为“泛型类型”,比如下面代码就定义了一个向量泛型类型

type Vector[T any] []T

这是一个带有类型参数的类型定义,类型参数位于类型名的后面,同样用方括号括起。在类型定义体中可以引用类型参数列表中的参数名(比如 T)。类型参数同样拥有自己的约束,如上面代码中的 any。在 Go 1.18 中,any 是 interface{}的别名,也是一个预定义标识符,使用 any 作为类型参数的约束,代表没有任何约束。

package main

import "fmt"

type Vector[T any] []T

func (v Vector[T]) Dump() {
	fmt.Printf("%#v\n", v)
}

func main() {
	var iv = Vector[int]{1, 2, 3, 4}
	var sv Vector[string]
	sv = []string{"a", "b", "c", "d"}
	iv.Dump()
	sv.Dump()
}

输出

main.Vector[int]{1, 2, 3, 4}
main.Vector[string]{"a", "b", "c", "d"}

参考

Comment ( 0 )

Sign in to post a comment

1
https://gitee.com/lucktk/study-nodes.git
git@gitee.com:lucktk/study-nodes.git
lucktk
study-nodes
micro-service
master

Search