Go語言 泛型 - 如何使用和解讀組合類型

2023-02-16 17:38 更新

在Go 1.18以前,Go只支持內置泛型。 從Go 1.18開始,Go也支持自定義泛型。 本文只介紹內置泛型。

Go通過各種一等公民組合類型來實現(xiàn)內置泛型。 我們可以用各種一等公民組合類型來組合出無窮個類型。 本文將展示一些自定義組合類型的例子并解釋如何解讀這些自定義類型。

一些復雜組合類型的例子

Go中的組合類型字面表示設計得非常直觀和易于解讀。 即使對于一些非常復雜的類型,我們也幾乎不可能在解讀它們的字面形式中迷失。 下面將從簡單到復雜列出一些自定義組合類型的例子并進行解讀。

先看一個簡單的例子:

[3][4]int

當解讀一個類型的字面形式時,我們應該從左到右進行解讀。 左邊開頭的[3]表示著這個類型為一個數(shù)組類型,它右邊的整個部分為它的元素類型。 對于這個例子,它的元素類型為另外一個數(shù)組類型[4]int。 此另外一個數(shù)組類型的元素類型為內置類型int。 第一個數(shù)組類型可以被看作是一個二維數(shù)組類型。

一個使用此數(shù)組類型的例子:

package main

import (
	"fmt"
)

func main() {
	matrix := [3][4]int{
		{1, 0, 0, 1},
		{0, 1, 0, 1},
		{0, 0, 1, 1},
	}

	matrix[1][1] = 3
	a := matrix[1] // 變量a的類型為[4]int
	fmt.Println(a) // [0 3 0 1]
}

類似的,

  • [][]string是一個元素類型為另一個切片類型[]string的切片類型。
  • **bool是一個基類型為另一個指針類型*bool的指針類型。
  • chan chan int是一個元素類型為另一個通道類型的chan int的通道類型。
  • map[int]map[int]string是一個元素類型為另一個映射類型map[int]string的映射類型。 這兩個映射類型的鍵值類型均為內置類型int。
  • func(int32) func(int32)是一個只有一個輸入參數(shù)和一個返回值的函數(shù)類型,此返回值的類型為一個只有一個輸入參數(shù)的函數(shù)類型。 這兩個函數(shù)類型的輸入參數(shù)的類型均為內置類型int32

下面是另一個自定義組合類型:

chan *[16]byte

最左邊的chan關鍵字表明此類型是一個通道類型。 chan關鍵字右邊的整個部分*[16]byte表示此通道類型的元素類型,此元素類型是一個指針類型。 此指針類型的基類型為*右邊的整個部分:[16]byte,此基類型為一個數(shù)組類型。 此數(shù)組類型的元素類型為內置類型byte。

一個使用此通道類型的例子:

package main

import (
	"fmt"
	"time"
	"crypto/rand"
)

func main() {
	c := make(chan *[16]byte)

	go func() {
		// 使用兩個數(shù)組以避免數(shù)據(jù)競爭。
		var dataA, dataB = new([16]byte), new([16]byte)
		for {
			_, err := rand.Read(dataA[:])
			if err != nil {
				close(c)
			} else {
				c <- dataA
				dataA, dataB = dataB, dataA
			}
		}
	}()

	for data := range c {
		fmt.Println((*data)[:])
		time.Sleep(time.Second / 2)
	}
}

類似的,類型map[string][]func(int) int為一個映射類型。 此映射類型的鍵值類型為內置類型string,右邊剩余的部分為此映射類型的元素類型。 []表明此映射的元素類型為一個切片類型,此切片類型的元素類型為一個函數(shù)類型func(int) int。

一個使用了此映射類型的例子:

package main

import "fmt"

func main() {
	addone := func(x int) int {return x + 1}
	square := func(x int) int {return x * x}
	double := func(x int) int {return x + x}

	transforms := map[string][]func(int) int {
		"inc,inc,inc": {addone, addone, addone},
		"sqr,inc,dbl": {square, addone, double},
		"dbl,sqr,sqr": {double, double, square},
	}

	for _, n := range []int{2, 3, 5, 7} {
		fmt.Println(">>>", n)
		for name, transfers := range transforms {
			result := n
			for _, xfer := range transfers {
				result = xfer(result)
			}
			fmt.Printf(" %v: %v \n", name, result)
		}
	}
}

下面是一個看上去有些復雜的類型:

[]map[struct {
	a int
	b struct {
		x string
		y bool
	}
}]interface {
	Build([]byte, struct {x string; y bool}) error
	Update(dt float64)
	Destroy()
}

讓我們從左到右解讀此類型。 最左邊開始的[]表明這是一個切片類型,緊跟著的map關鍵字表明此切片類型的元素為一個映射類型。 map關鍵字后緊跟的一對方括號[]中的結構體類型字面形式表明此映射的鍵值類型為一個結構體類型。 此中括號右邊的整個部分表明此映射的元素類型為一個接口類型。此接口類型指定了三個方法。 此映射的鍵值結構體類型有兩個字段,第一個字段的名稱和類型為a和內置類型int; 第二個字段的名稱為b,它的類型為另外一個結構體類型struct {x string; y bool}。 此另外一個結構體類型也有兩個字段:內置string類型的字段x和內置bool類型的字段y。

請注意第二個結構體類型也被用做剛提及的接口類型所指定的其中一個方法中的其中一個參數(shù)類型。

我們經常將復雜類型的各個組成部分單獨提前聲明為一個類型名,從而獲得更高的可讀性。 下面的代碼中的類型別名T和上面剛解讀的類型表示同一個類型。

type B = struct {
	x string
	y bool
}

type K = struct {
	a int
	b B
}

type E = interface {
	Build([]byte, B) error
	Update(dt float64)
	Destroy()
}

type T = []map[K]E

內置泛型函數(shù)

Go中當前的內置泛型除了上述類型組合,還有一些支持泛型的內置函數(shù)。 比如,內置函數(shù)len可以用來獲取各種容器值的長度。 unsafe標準庫包中的函數(shù)也可以被看作是支持泛型的內置函數(shù)。 這些函數(shù)在前面的各篇文章中已經一一介紹過了。

自定義泛型

從1.18版本開始,Go已經支持自定義泛型。 請閱讀《Go自定義泛型101》一書來了解如何使用自定義泛型。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號