在Go 1.18以前,Go只支持內(nèi)置泛型。 從Go 1.18開(kāi)始,Go也支持自定義泛型。 本文只介紹內(nèi)置泛型。
Go通過(guò)各種一等公民組合類型來(lái)實(shí)現(xiàn)內(nèi)置泛型。 我們可以用各種一等公民組合類型來(lái)組合出無(wú)窮個(gè)類型。 本文將展示一些自定義組合類型的例子并解釋如何解讀這些自定義類型。
Go中的組合類型字面表示設(shè)計(jì)得非常直觀和易于解讀。 即使對(duì)于一些非常復(fù)雜的類型,我們也幾乎不可能在解讀它們的字面形式中迷失。 下面將從簡(jiǎn)單到復(fù)雜列出一些自定義組合類型的例子并進(jìn)行解讀。
先看一個(gè)簡(jiǎn)單的例子:
[3][4]int
當(dāng)解讀一個(gè)類型的字面形式時(shí),我們應(yīng)該從左到右進(jìn)行解讀。 左邊開(kāi)頭的[3]
表示著這個(gè)類型為一個(gè)數(shù)組類型,它右邊的整個(gè)部分為它的元素類型。 對(duì)于這個(gè)例子,它的元素類型為另外一個(gè)數(shù)組類型[4]int
。 此另外一個(gè)數(shù)組類型的元素類型為內(nèi)置類型int
。 第一個(gè)數(shù)組類型可以被看作是一個(gè)二維數(shù)組類型。
一個(gè)使用此數(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
是一個(gè)元素類型為另一個(gè)切片類型[]string
的切片類型。
**bool
是一個(gè)基類型為另一個(gè)指針類型*bool
的指針類型。
chan chan int
是一個(gè)元素類型為另一個(gè)通道類型的chan int
的通道類型。
map[int]map[int]string
是一個(gè)元素類型為另一個(gè)映射類型map[int]string
的映射類型。
這兩個(gè)映射類型的鍵值類型均為內(nèi)置類型int
。
func(int32) func(int32)
是一個(gè)只有一個(gè)輸入?yún)?shù)和一個(gè)返回值的函數(shù)類型,此返回值的類型為一個(gè)只有一個(gè)輸入?yún)?shù)的函數(shù)類型。
這兩個(gè)函數(shù)類型的輸入?yún)?shù)的類型均為內(nèi)置類型int32
。
下面是另一個(gè)自定義組合類型:
chan *[16]byte
最左邊的chan
關(guān)鍵字表明此類型是一個(gè)通道類型。 chan
關(guān)鍵字右邊的整個(gè)部分*[16]byte
表示此通道類型的元素類型,此元素類型是一個(gè)指針類型。 此指針類型的基類型為*
右邊的整個(gè)部分:[16]byte
,此基類型為一個(gè)數(shù)組類型。 此數(shù)組類型的元素類型為內(nèi)置類型byte
。
一個(gè)使用此通道類型的例子:
package main
import (
"fmt"
"time"
"crypto/rand"
)
func main() {
c := make(chan *[16]byte)
go func() {
// 使用兩個(gè)數(shù)組以避免數(shù)據(jù)競(jìng)爭(zhēng)。
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
為一個(gè)映射類型。 此映射類型的鍵值類型為內(nèi)置類型string
,右邊剩余的部分為此映射類型的元素類型。 []
表明此映射的元素類型為一個(gè)切片類型,此切片類型的元素類型為一個(gè)函數(shù)類型func(int) int
。
一個(gè)使用了此映射類型的例子:
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)
}
}
}
下面是一個(gè)看上去有些復(fù)雜的類型:
[]map[struct {
a int
b struct {
x string
y bool
}
}]interface {
Build([]byte, struct {x string; y bool}) error
Update(dt float64)
Destroy()
}
讓我們從左到右解讀此類型。 最左邊開(kāi)始的[]
表明這是一個(gè)切片類型,緊跟著的map
關(guān)鍵字表明此切片類型的元素為一個(gè)映射類型。 map
關(guān)鍵字后緊跟的一對(duì)方括號(hào)[]
中的結(jié)構(gòu)體類型字面形式表明此映射的鍵值類型為一個(gè)結(jié)構(gòu)體類型。 此中括號(hào)右邊的整個(gè)部分表明此映射的元素類型為一個(gè)接口類型。此接口類型指定了三個(gè)方法。 此映射的鍵值結(jié)構(gòu)體類型有兩個(gè)字段,第一個(gè)字段的名稱和類型為a
和內(nèi)置類型int
; 第二個(gè)字段的名稱為b
,它的類型為另外一個(gè)結(jié)構(gòu)體類型struct {x string; y bool}
。 此另外一個(gè)結(jié)構(gòu)體類型也有兩個(gè)字段:內(nèi)置string
類型的字段x
和內(nèi)置bool
類型的字段y
。
請(qǐng)注意第二個(gè)結(jié)構(gòu)體類型也被用做剛提及的接口類型所指定的其中一個(gè)方法中的其中一個(gè)參數(shù)類型。
我們經(jīng)常將復(fù)雜類型的各個(gè)組成部分單獨(dú)提前聲明為一個(gè)類型名,從而獲得更高的可讀性。 下面的代碼中的類型別名T
和上面剛解讀的類型表示同一個(gè)類型。
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
Go中當(dāng)前的內(nèi)置泛型除了上述類型組合,還有一些支持泛型的內(nèi)置函數(shù)。 比如,內(nèi)置函數(shù)len
可以用來(lái)獲取各種容器值的長(zhǎng)度。 unsafe
標(biāo)準(zhǔn)庫(kù)包中的函數(shù)也可以被看作是支持泛型的內(nèi)置函數(shù)。 這些函數(shù)在前面的各篇文章中已經(jīng)一一介紹過(guò)了。
從1.18版本開(kāi)始,Go已經(jīng)支持自定義泛型。 請(qǐng)閱讀《Go自定義泛型101》一書(shū)來(lái)了解如何使用自定義泛型。
更多建議: