Golang泛型的使用方法詳解
Go 和Python語言不同,處理不同數據類型非常嚴格。如Python可以定義函數帶兩個數值類型并返回較大的數值,但可以不嚴格指定參數類型為float或integer。同樣功能go1.18之前版本需要定義兩個函數分別處理對應類型,通過泛型可以實現上面描述功能,無需為每種類型重復定義函數。
本文通過示例學習Go泛型,包括三個部分:非泛型函數,泛型函數,泛型類型約束。
泛型的作用
我們知道Go一些函數(如fmt.Println)使用空interface支持多種數據類型,但需要開發(fā)者寫大量代碼實現很多函數或方法支持多種自定義類型不是最佳選項,于是泛型登場,提供新的解決方案,無需使用接口和反射支持多種數據類型。
下面通過示例來說明:
package main
import (
"fmt"
)
func Print[T any](s []T) {
for _, v := range s {
fmt.Print(v, " ")
}
fmt.Println()
}
func main() {
Ints := []int{1, 2, 3}
Strings := []string{"One", "Two", "Three"}
Print(Ints)
Print(Strings)
}
上面代碼種Print()函數使用泛型,通過在函數名稱和參數之間增加[T any]指定泛型變量, 既然T 為any類型,函數可傳入任何數據類型slice。當然Print函數不能處理slice之外的輸入,這是因為函數實現決定的。通過泛型函數可以避免針對每種數據類型編寫函數,這種通用的思想就是泛型。
非泛型函數
我們實現兩個函數,返回數值類型數組元素的和。我們至少創(chuàng)建兩個函數,一個針對float64類型,另一個為int64,因此工作量翻倍。
package main
import "fmt"
func main() {
f := []float64{1.0, 2.0, 3.0, 4.0, 5.0}
i := []int64{1, 2, 3, 4, 5}
s1 := SumOfFloat(f)
s2 := SumOfIntegers(i)
fmt.Println("Sum for float64 :", s1)
fmt.Println("Sum for int64 :", s2)
}
func SumOfFloat(nums []float64) float64 {
var sum float64
for _, num := range nums {
sum += num
}
return sum
}
func SumOfIntegers(nums []int64) int64 {
var sum int64
for _, num := range nums {
sum += num
}
return sum
}
運行輸出結果:
$ go run main.go
Sum for float64 : 15
Sum for int64 : 15
上面代碼針對兩種類型定義了兩個函數,返回各自參數對應類型數組元素之和。下面通過泛型合并兩個函數為一個,節(jié)約時間和精力。
泛型函數
本節(jié)實現單個函數,參數仍然是數組,但類型可以為float64或int64,并返回數組元素之和,從而可以代替上面兩個函數。
為了能夠定義函數接收float64和int64兩種類型,新的泛型函數需要聲明接收的類型,即調用代碼需要判斷類型是否為float64或int64。為此,新的函數簽名需要有點變化,除了普通的參數外,還需要聲明類型參數,從而轉換函數為泛型函數支持不同類型。
每個形參都有一個類型約束,它指定調用代碼可用于形參允許的實參類型。在編譯時這些參數表示的類型就是調用代碼支持的類型。當然泛型函數的實現邏輯需要支持形參聲明的所有類型。舉例,泛型函數支持字符串和數值參數,函數實現邏輯需要對參數進行索引,顯然數值無法索引將引起編譯錯誤。
泛型函數語法:
func genericFunction[T any](s []T) []T{
}
解釋如下:
上面語法種在名稱和參數之間的[]用于指定形式參數類型,可以是一組類型或一個約束接口。T 參數類型,用于定義形式參數和返回值類型。
函數內部也可以訪問參數,any 是一個interface,T必須實現該接口。Go1.18版本引入any,底層引用空接口,即interface{}。 下面舉例說明:
package main
import "fmt"
func main() {
f := []float64{1.0, 2.0, 3.0, 4.0, 5.0}
i := []int64{1, 2, 3, 4, 5}
s1 := genericSum(f)
s2 := genericSum(i)
fmt.Println("Sum for float64 :", s1)
fmt.Println("Sum for int64 :", s2)
}
func genericSum[N int64 | float64](nums []N) N {
var sum N
for _, num := range nums {
sum += num
}
return sum
}
輸出結果:
$ go run main.go
Sum for float64 : 15
Sum for int64 : 15
上面示例中演示定義泛型函數genericSum。參數類型在[]中聲明的泛型類型。N聲明在形參和返回值中使用,N是新的類型,被限定了兩種類型float64 和 int64的聯合類型。
在函數體類,首先聲明N類型變量,然后遍歷數組計算綜合并返回。
假設泛型函數支持很多種類型,比如:int, int8 , int26, int32, int64, float32, float64。這樣函數簽名變得非常冗長難看,我們可以定義一個類型包括上述類型,本質上就是把聯合類型遷移至函數聲明外面。
package main
import "fmt"
type Number interface {
int64 | float64
}
func main() {
f := []float64{1.0, 2.0, 3.0, 4.0, 5.0}
i := []int64{1, 2, 3, 4, 5}
s1 := genericSum(f)
s2 := genericSum(i)
fmt.Println("Sum for float64 :", s1)
fmt.Println("Sum for int64 :", s2)
}
func genericSum[N Number](nums []N) N {
var sum N
for _, num := range nums {
sum += num
}
return sum
}
總結
本文是關于Go泛型的,因為泛型是Golang的新成員。通過對比帶泛型和不帶泛型的函數,泛型可以很明顯地節(jié)省時間和精力。泛型不是接口的替代品,兩者被設計為一起工作,避免重復代碼,讓Go類型更安全、代碼更整潔。
到此這篇關于Golang泛型的使用方法詳解的文章就介紹到這了,更多相關Golang泛型內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用client-go工具調用kubernetes API接口的教程詳解(v1.17版本)
這篇文章主要介紹了使用client-go工具調kubernetes API接口(v1.17版本),本文通過圖文實例相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08

