golang1.21泛型函數(shù)全面講解
Go 1.21中的泛型基本語(yǔ)法
要定義泛型函數(shù)或類型,可以使用類型 T關(guān)鍵字,后跟用方括號(hào)[]括起來(lái)的泛型形參的名稱。例如,要?jiǎng)?chuàng)建一個(gè)接受任意類型的slice并返回其第一個(gè)元素的泛型函數(shù),可以這樣定義:
func First[T any](items []T) T {
return items[0]
}在上面的例子中,[T any]表示類型參數(shù)T,它表示任意類型。any關(guān)鍵字表示T類型可以是任何有效類型。
然后,可以使用任何切片類型調(diào)用First函數(shù),該函數(shù)將返回該切片的第一個(gè)元素。例如:
func func1() {
intSlice := []int{1, 2, 3, 4, 5}
firstInt := First[int](intSlice) // returns 1
println(firstInt)
stringSlice := []string{"apple", "banana", "cherry"}
firstString := First[string](stringSlice) // returns "apple"
println(firstString)
}
func First[T any](items []T) T {
return items[0]
}注意,在調(diào)用泛型函數(shù)時(shí),我們?cè)诜嚼ㄌ?hào)[]中指定類型參數(shù)。這允許編譯器在編譯期間為該類型生成特定的代碼。
我們還可以向泛型類型參數(shù)添加約束,將其限制為特定類型。例如,如果我們想將類型T限制為僅實(shí)現(xiàn)Stringer接口的類型,可以使用如下約束:
func PrintString[T Stringer](value T) {
fmt.Println(value.String())
}Stringer約束確保類型T必須具有String()方法。這個(gè)約束允許我們?cè)诤瘮?shù)的value參數(shù)上安全地調(diào)用String()方法。
在Go 1.21中具有各種類型的泛型
在另一個(gè)示例中,讓我們編寫函數(shù)SumGenerics,它對(duì)各種數(shù)字類型(如int、int16、int32、int64、int8、float32和float64)執(zhí)行加法操作。
func SumGenerics[T int | int16 | int32 | int64 | int8 | float32 | float64](a, b T) T {
return a + b
}讓我們看看如何利用這個(gè)泛型函數(shù):
func func2() {
sumInt := SumGenerics[int](2, 3)
sumFloat := SumGenerics[float32](2.5, 3.5)
sumInt64 := SumGenerics[int64](10, 20)
fmt.Println(sumInt) // returns 5
fmt.Println(sumFloat) // returns 6.0
fmt.Println(sumInt64) // returns 30
}在上面的代碼中,我們可以看到,通過(guò)在調(diào)用泛型函數(shù)時(shí)在方括號(hào)[]中指定類型參數(shù),我們可以對(duì)不同的數(shù)字類型執(zhí)行加法操作。類型約束確保只有指定的類型[T int, int16, int32, int64, int8, float32,或float64]可以用作類型參數(shù)。
以這種方式使用泛型使我們能夠在不犧牲類型安全的情況下編寫簡(jiǎn)潔且可重用的代碼??梢允褂酶鞣N數(shù)字類型調(diào)用該函數(shù),編譯器將為每種類型生成特定的代碼,以確保正確執(zhí)行加法操作。
Go 1.21中具有任意數(shù)據(jù)類型的泛型
泛型可以用于任意數(shù)據(jù)類型的序列化和反序列化,使用提供的序列化和反序列化函數(shù):
type Person struct {
Name string
Age int
Address string
}
func Serialize[T any](data T) ([]byte, error) {
buffer := bytes.Buffer{}
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(data)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func Deserialize[T any](b []byte) (T, error) {
buffer := bytes.Buffer{}
buffer.Write(b)
decoder := gob.NewDecoder(&buffer)
var data T
err := decoder.Decode(&data)
if err != nil {
return data, err
}
return data, nil
}在本例中,我們有兩個(gè)通用函數(shù)Serialize和Deserialize,它們利用Go的gob包將任意數(shù)據(jù)類型轉(zhuǎn)換為字節(jié),反之亦然。
func DeserializeUsage() {
person := Person{
Name: "John",
Age: 30,
Address: "123 Main St.",
}
serialized, err := Serialize(person)
if err != nil {
panic(err)
}
deserialized, err := Deserialize[Person](serialized)
if err != nil {
panic(err)
}
fmt.Printf("Name: %s, Age: %d, Address: %s", deserialized.Name, deserialized.Age, deserialized.Address)
}Output: Name: John, Age: 30, Address: 123 Main St.
在上面的代碼中,我們用一些數(shù)據(jù)創(chuàng)建了一個(gè)Person實(shí)例。然后使用Serialize函數(shù)將person對(duì)象轉(zhuǎn)換為字節(jié)數(shù)組。稍后,使用Deserialize函數(shù),將字節(jié)數(shù)組轉(zhuǎn)換回Person對(duì)象。
通過(guò)將序列化和反序列化函數(shù)定義為具有T any類型參數(shù)的泛型函數(shù),我們可以序列化和反序列化任何支持使用gob包進(jìn)行編碼和解碼的數(shù)據(jù)類型。
在Go中使用泛型和Validate函數(shù)自定義驗(yàn)證器
讓我們用自定義驗(yàn)證器編寫一個(gè)通用的Validate函數(shù)。
type Validator[T any] func(T) error
func Validate[T any](data T, validators ...Validator[T]) error {
for _, validator := range validators {
err := validator(data)
if err != nil {
return err
}
}
return nil
}在本例中,我們有一個(gè)通用的Validate函數(shù),它使用自定義驗(yàn)證器執(zhí)行數(shù)據(jù)驗(yàn)證。Validator類型表示一個(gè)函數(shù),它接受任意類型T的值并返回一個(gè)錯(cuò)誤。
func StringNotEmpty(s string) error {
if len(strings.TrimSpace(s)) == 0 {
return fmt.Errorf("string cannot be empty")
}
return nil
}
func IntInRange(num int, min, max int) error {
if num < min || num > max {
return fmt.Errorf("number must be between %d and %d", min, max)
}
return nil
}此外,我們有兩個(gè)自定義驗(yàn)證器示例:StringNotEmpty和IntInRange。
StringNotEmpty確保字符串不為空,IntInRange檢查整數(shù)是否在指定范圍內(nèi)。
package main
func main() {
person := Person{
Name: "John",
Age: 30,
Address: "123 Main St.",
}
err := Validate(person, func(p Person) error {
return StringNotEmpty(p.Name)
}, func(p Person) error {
return IntInRange(p.Age, 0, 120)
})
if err != nil {
println(err.Error())
panic(err)
}
println("Person is valid")
}在本例中,我們創(chuàng)建了一個(gè)Person實(shí)例,并將其傳遞給Validate函數(shù)。我們?yōu)?code>Person結(jié)構(gòu)定義了兩個(gè)自定義驗(yàn)證器,檢查Name字段是否為空,Age字段是否在有效范圍內(nèi)。如果任何驗(yàn)證器返回錯(cuò)誤,驗(yàn)證過(guò)程將停止,并返回相應(yīng)的錯(cuò)誤。
通過(guò)使用泛型和自定義驗(yàn)證器,Validate函數(shù)允許跨不同數(shù)據(jù)類型進(jìn)行靈活和可重用的數(shù)據(jù)驗(yàn)證,增強(qiáng)代碼可重用性,并使添加或修改驗(yàn)證規(guī)則變得容易。
讓我們?cè)賹懸粋€(gè)使用validator函數(shù)的例子
type LoginForm struct {
Username string
Password string
}
func (f *LoginForm) Validate() error {
return Validate(f,
func(l *LoginForm) error {
return StringNotEmpty(l.Username)
},
func(l *LoginForm) error {
return StringNotEmpty(l.Password)
},
)
}
func ValidateUsage2() {
loginForm := LoginForm{
Username: "John",
Password: "123",
}
err := loginForm.Validate()
if err != nil {
println(err.Error())
panic(err)
}
println("Login form is valid")
}在本例中,LoginForm結(jié)構(gòu)實(shí)現(xiàn)了一個(gè)Validate方法,該方法利用了我們之前定義的Validate泛型函數(shù)。
Validate方法調(diào)用通用的Validate函數(shù),并為它提供兩個(gè)特定于LoginForm類型的自定義驗(yàn)證器。驗(yàn)證器表示為閉包函數(shù),使用StringNotEmpty驗(yàn)證器函數(shù)檢查Username和Password字段是否為空。
要驗(yàn)證LoginForm實(shí)例,只需在實(shí)例本身上調(diào)用validate方法。
package main
func main() {
loginForm := LoginForm{
Username: "John",
Password: "123",
}
err := loginForm.Validate()
if err != nil {
println(err.Error())
panic(err)
}
println("Login form is valid")
}如果任何驗(yàn)證器返回錯(cuò)誤,驗(yàn)證過(guò)程將停止,并返回相應(yīng)的錯(cuò)誤。在這種情況下,我們相應(yīng)地處理錯(cuò)誤。
總結(jié)
這些示例展示了Go 1.21中泛型的強(qiáng)大功能和多功能性。泛型使我們能夠編寫可重用和類型安全的代碼,這些代碼可以處理不同的數(shù)據(jù)類型和結(jié)構(gòu),而不會(huì)犧牲代碼的清晰度和可維護(hù)性。
泛型為Go編程語(yǔ)言帶來(lái)了顯著的好處,增強(qiáng)了代碼重用,減少了冗余,并改進(jìn)了代碼組織。有了泛型,開發(fā)人員就能夠編寫更有表現(xiàn)力、更簡(jiǎn)潔、更靈活的代碼,以適應(yīng)不同的數(shù)據(jù)類型和結(jié)構(gòu),從而為更具可擴(kuò)展性和可維護(hù)性的軟件開發(fā)鋪平道路。
以上就是golang1.21泛型函數(shù)全面講解的詳細(xì)內(nèi)容,更多關(guān)于go1.21泛型函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang操作elasticsearch的實(shí)現(xiàn)
這篇文章主要介紹了golang操作elasticsearch,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
golang使用json格式實(shí)現(xiàn)增刪查改的實(shí)現(xiàn)示例
這篇文章主要介紹了golang使用json格式實(shí)現(xiàn)增刪查改的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
Go語(yǔ)言copy()實(shí)現(xiàn)切片復(fù)制
本文主要介紹了Go語(yǔ)言copy()實(shí)現(xiàn)切片復(fù)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Go語(yǔ)言bufio庫(kù)的全面指南與實(shí)戰(zhàn)技巧詳解
這篇文章主要為大家全面介紹一下?bufio?庫(kù)的核心組件與功能,包括?Reader、Writer?和?Scanner?等并深入探討它們?cè)趯?shí)際編程中的運(yùn)用場(chǎng)景和技巧,感興趣的可以了解下2024-01-01
golang獲取prometheus數(shù)據(jù)(prometheus/client_golang包)
本文主要介紹了使用Go語(yǔ)言的prometheus/client_golang包來(lái)獲取Prometheus監(jiān)控?cái)?shù)據(jù),具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
使用Go語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的無(wú)界資源池
本文我們希望通過(guò)go語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的資源池,而這個(gè)資源池的資源包括但不限于數(shù)據(jù)庫(kù)連接池,線程池,協(xié)程池,網(wǎng)絡(luò)連接池,只要這些資源實(shí)現(xiàn)我們指定的關(guān)閉方法,則都可以通過(guò)我們封裝的資源池進(jìn)行統(tǒng)一管理,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05
解析Go語(yǔ)言編程中的struct結(jié)構(gòu)
這篇文章主要介紹了Go語(yǔ)言編程中的struct結(jié)構(gòu),是Go語(yǔ)言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10

