承接前篇泛型
你可以用泛型來定義可以接受不同型別的數據結構,如:Slice[T]。
透過傳入型別 Type Argument,泛型型別可以被實例化成具體的型別,如:Slice[int]。
透過型別約束 Type Constraint,可以限制型別形參的選擇,確保型別安全。
泛型示例:
假設我們目前有:
type stringSlice []string
var a stringSlice = []string{"A", "B", "C"} //沒錯
var b stringSlice = []int{1, 2, 3} //報錯
這是因為 stringSlice 型別為 []string,但如果今天我們想要,[]float64、[]int 呢 ?
也算簡單:
type stringSlice []string
type float64Slice []float64
type intSlice []int
但有更簡潔寫法 :
type Slice[T string|float64|int ] []T
T : Type parameter,代表具體類型並不確定。
string|float64|int 這部分被稱為 Type constraint,中間的 | 的意思是告訴編譯器,T 只可以接收 int 或 string 或 float64 這三種類型。
[]中括號裡的 T string|float64|int 這一整串因為定義了所有的 type parameters (在這個例子裡只有一個Type Parameter),所以我們稱其為 type parameter list 。
而這裡新定義的型別名稱叫 Slice[T]。
假設我們需要定義一個Map函數,則該函數需要兩種類型:Key 和 Value。
type Map[K T, V T] struct {
keys []K
values []V
}
在這個例子中,[K T, V T]是一個type parameter list。它包含了兩個type parameters:K 和 V。每個type parameter 都有自己的約束(在此例中,使用了 T 作為通用約束)。
如果想給每個type parameter加上特定的約束:
type Map[K string|int, V float64|bool] struct {
keys []K
values []V
}
在這裡,K 可以是 string 或 int,而 V 可以是 float64 或 bool。
而這種帶有 type parameter 的型別,就被稱之為泛型(generic),且不能直接拿来使用,
需要傳入 Type argument 確定具體型別後才可用,而傳入具體型別的這個行為,稱做"實例化(Instantiations)"
var a Slice[T int|float32|float64] []T
// 這裡傳入了type parameter : int,泛型型別Slice[T]被實例化為 Slice[int]
var a Slice[int] = []int{1, 2, 3}
fmt.Printf("Type Name: %T",a) //輸出:Type Name: Slice[int]
// 傳入type parameter : float32, 將泛型型別Slice[T]實例化為 Slice[float32]
var b Slice[float32] = []float32{1.0, 2.0, 3.0}
fmt.Printf("Type Name: %T",b) //輸出:Type Name: Slice[float32]
// 錯誤。因為變數a的型別為Slice[int],b的型別為Slice[float32],兩者型別不同
a = b
// ✗ 錯誤。string不在型別約束 int|float32|float64 中,不能用來實例化泛型型別
var c Slice[string] = []string{"Hello", "World"}
// ✗ 錯誤。Slice[T]是泛型型別,不可直接使用必須實例化為具體的型別
var x Slice[T] = []int{1, 2, 3}
範例 :
package main
import (
"fmt"
)
type testMap[KEY string | int, VALUE float32 | float64 ] map[KEY]VALUE
func main() {
var a testMap[string, float64] = map[string]float64{
"jack": 75.3,
"Ricky": 72.5,
}
// 取得 "jack" 的分數
jackScore := a["jack"]
fmt.Println("Jack's score:", jackScore)
// 取得 "Ricky" 的分數
rickyScore := a["Ricky"]
fmt.Println("Ricky's score:", rickyScore)
}