網頁

2022/4/7

Golang 泛型簡介 generics intro

Go語言的泛型(generics)簡介。


簡介

Go未加入泛型以前函式的參數只能有一型態,雖然空介面參數或實作介面參數能達到參數多樣(多型polymorphism)的效果但有些不足。空介面參數要先轉型且無法做編譯檢查;實作介面參數的類型必須實作每一個介面方法非常不便。Go 1.18加入泛型後讓函式的參數型態可為多樣且編譯時檢查型態避免執行期發生錯誤。


範例

Go透過在函式名稱後定義type parameters來達到泛型效果。下面的Add()即為定義泛型型態的泛型函式(generic function)。

函式Add()的名稱與參數間的方括弧敘述([T int | float64])為泛型定義,T為type parameter,int | float64type constraints,意思是type parameter T的型態只能是intfloat64

呼叫泛型函式時在函式名稱與引數間指定引數的泛型型態稱為type arguments,只有符合型態的引數才能傳入。例如下面的Add[int](1, 2)[int]即是type argument;Add[float64](1.1, 2.2)的type argument為float64。或呼叫時由引數的型態來推斷則可省略type arguments的敘述,例如下面的Add(1, 2)Add(1.1, 2.2)皆省略了type argument。

main.go

package main

import "fmt"

func main() {
    // call generic function with type arguments
    fmt.Println(Add[int](1, 2))         // 3
    fmt.Println(Add[float64](1.0, 2.2)) // 3.2

    // call generic function without type arguments
    fmt.Println(Add(1, 2))     // 3
    fmt.Println(Add(1.0, 2.2)) // 3.2
}

func Add[T int | float64](x, y T) T {
    return x + y
}

Type constraints也可抽出另外定義為interface。例如下面把Add()函式的type constraints改定義在interface Num,則type parameter T的constraint改為Num

main.go

package main

import "fmt"

type Num interface {
    int | float64 // type constraints
}

func main() {
    // call generic function with type arguments
    fmt.Println(Add[int](1, 2))         // 3
    fmt.Println(Add[float64](1.0, 2.2)) // 3.2

    // call generic function without type arguments
    fmt.Println(Add(1, 2))     // 3
    fmt.Println(Add(1.0, 2.2)) // 3.2
}

func Add[T Num](x, y T) T {
	return x + y
}

Generic的interface constraint可用來定義generic type的slice。注意下面var ints Nums[int][int]是type arguments,覺得應該和呼叫函式一樣可自行推斷才是。

PrintMap()函式的type constraint comparable代表「非interface的可比較型態」,即任何可用=!=運算符比較的型態皆屬於comparable,如intstring

main.go

package main

import "fmt"

type Num interface {
    int | float64 // type constraints
}

type Nums[T Num] []T // slice of generic type

func main() {
 
    var ints Nums[int] = []int{1, 2, 3}
    PrintAll[int](ints)

    var floats Nums[float64] = []float64{1.1, 2.2, 3.3}
    PrintAll(floats)
    
    m := map[string]Nums[int]{
        "a": {1, 2},
        "b": {3, 4},
    }
    
    PrintMap(m)
}

func PrintAll[T Num](nums Nums[T]) {
    for _, n := range nums {
        fmt.Println(n)
    }
}

func PrintMap[K comparable, V Num](m map[K]Nums[V]) {
    for k, v := range m {
        fmt.Printf("key=%v, value=%v\n", k, v)
    }
}

github


沒有留言:

張貼留言