返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

8. 泛型

发布于 2024-10-12 19:15:48 字数 3861 浏览 0 评论 0 收藏 0

泛型(generic)是一种代码复用技术,有时也称作模板(template)。允许在强类型语言代码中,使用实例化时才指定的 类型参数 (type parameter)。

  • 函数和类型(含接口)支持类型参数,方法暂不支持。
  • 支持推导,可省略类型实参(type argument)。
  • 通常以单个大写字母命名类型参数。
  • 类型参数必须有约束(constraints)。
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 Data[T any] struct {
	x T
}

func (d Data[T]) test() {
	fmt.Println(d)
}

// func (d Data[T]) test2[X any](x X) {}
//                       ~~~~~~~ method must have no type parameters

func main() {
	d := Data[int]{ x: 1 }
	d.test()
}

可使用接口对类型参数进行约束,指示它能做什么。

如果是 any ,表示可传入任意类型对象。

其实 [T any] 还不如简写成 [T]

约束直接写在 [] 里,可能导致函数签名很难看,不如有个 where 子句更优雅。

希望后续版本能有所改变。

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{})
}

类型集合

接口的两种定义:

  • 普通接口 :方法集合(method sets),指示能做什么。
  • 类型约束 :类型集合(type sets),指示谁来做。

相比普通接口 “被动、隐式” 实现,类型约束显式指定实现接口的类型集合。

type Integer interface {
	Signed | Unsigned
}

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}
  • 竖线:类型集,匹配其中任一类型即可。
  • 波浪线:底层类型(underlying type)是该类型的所有类型。
func main() {
	// println(maxValue(struct{}{}, struct{}{}))
    //                  ~~~~~~~~ struct{} does not satisfy cmp.Ordered
}

普通接口可用作约束,但类型约束却不能当普通接口使用。

func main() {
	// var x cmp.Ordered = 1
	//       ~~~~~~~~~~~~~~~~~~~ interface contains type constraints
}

当然,类型约束除类型集合外,还可以有方法声明。

type A int
type B string

func (a A) Test() { println("A:", a) }
func (b B) Test() { println("B:", b) }

// -----------------------------------

type Tester interface {
	A | B

	Test()                   // 集合里的类型需要实现!
}

func test[T Tester](x T) {
	x.Test()                 // 约束调用!
}

// -----------------------------------

func main() {
	test[A](1)              // test(A(1))
	test[B]("abc")          // test(B("abc"))

	// var c Tester = A(1)
	//       ~~~~~~ interface contains type constraints
	// c.Test()
}

类型约束

除含类型集合的接口类型外,也可直接写入参数列表。

[T any]            // 任意类型。
[T int]            // 只能是 int。
[T ~int]           // 是 int 或底层类型是 int 的类型。
[T int | string]   // 只能是 int 或 string。
[T io.Reader]      // 任何实现 io.Reader 接口的类型。
func test[T int | float32](x T) {
	fmt.Printf("%T, %v\n", x, x)
}

func main() {
	test(1)
	test[float32](1.1)
	
	// test("abc")
	//       ~~~ string does not satisfy int|float32
}
func makeSlice[T int | float64](x T) []T {
	s := make([]T, 10)
	for i := 0; i < cap(s); i++ {
		s[i] = x
	}

	return s
}
func test[T int | float64, E ~[]T](x E) {
	for v := range x {
		fmt.Println(v)
	}
}

func main() {
	test([]int{1, 2, 3})
	test([]float64{1.1, 1.2, 1.3})
}

如类型约束不是接口,则无法调用其成员。

type Num int
func (n Num) print() { println(n) }

func test[T Num](n T) {
	// n.print()
	// ~~~~~~~ n.print undefined (type T has no field or method print)
}

func main() {
	test(1)
}

类型转换

不支持 switch 类型推断(type assert),须转换为普通接口。

func test[T any](x T) {
    
	// switch x.(type) {}
	//          ~~~~ cannot use type switch on type parameter value x

	// 转换为普通接口 ----------
    
	switch any(x).(type) {
	case int: println("int", x)
	}
}

An Introduction To Generics

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文