一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語(yǔ)言編程技術(shù)及教程分享平臺(tái)!
分類(lèi)導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Golang - 淺談Go1.18中的泛型編程

淺談Go1.18中的泛型編程

2022-01-26 11:24ink19 Golang

本文主要介紹了Go1.18中的泛型編程,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

前言

經(jīng)過(guò)這幾年的千呼萬(wàn)喚,簡(jiǎn)潔的Go語(yǔ)言終于在1.18版本迎來(lái)泛型編程。作為一門(mén)已經(jīng)有了14年歷史的強(qiáng)類(lèi)型語(yǔ)言,很難相信它到現(xiàn)在才開(kāi)始有一個(gè)正式的泛型。

淺談Go1.18中的泛型編程

以前的Go泛型

雖然直到1.18版本才加入泛型,但是在2014年便有相關(guān)的討論要在Go中加入泛型設(shè)計(jì)。但是由于各種原因沒(méi)有實(shí)現(xiàn)。而之后的接口(interface)的提出,讓泛型進(jìn)一步擱置。但是由于接口的缺陷,最終Go團(tuán)隊(duì)還是在1.18的版本中加入了泛型。實(shí)際上,這一版本的泛型設(shè)計(jì)在語(yǔ)言層面和接口非常相似(在實(shí)現(xiàn)層面肯定是不一樣的,泛型是編譯時(shí),接口是運(yùn)行時(shí)),對(duì)于他們之間的差異,也會(huì)在后面提到。

本文主要講述1.18beta1版本中的泛型,后續(xù)有改動(dòng),可能會(huì)更改文章。

泛型是什么

在我看來(lái)泛型其實(shí)用C++的模板一詞來(lái)描述就非常的準(zhǔn)確。在寫(xiě)代碼的時(shí)候,我們經(jīng)常需要寫(xiě)很多重復(fù)的邏輯,一般這個(gè)時(shí)候我們就會(huì)使用函數(shù)來(lái)對(duì)其進(jìn)行封裝。但是由于Go是一種強(qiáng)類(lèi)型語(yǔ)言,所以在定義和書(shū)寫(xiě)函數(shù)的時(shí)候需要在調(diào)用前標(biāo)明類(lèi)型。當(dāng)然如果這一重復(fù)的邏輯只需要固定的類(lèi)型,這樣就足夠了,但是很多時(shí)候我們需要不同的類(lèi)型進(jìn)行類(lèi)似的邏輯,譬如我們剛剛看到的GIF。對(duì)于普通開(kāi)發(fā)人員來(lái)說(shuō)這種情況可能遇到的比較少,但是在一些庫(kù)開(kāi)發(fā)人員來(lái)說(shuō),這種情況變得非常的普遍。

泛型程序設(shè)計(jì)(generic programming)是程序設(shè)計(jì)語(yǔ)言的一種風(fēng)格或范式。泛型允許程序員在強(qiáng)類(lèi)型程序設(shè)計(jì)語(yǔ)言中編寫(xiě)代碼時(shí)使用一些以后才指定的類(lèi)型,在實(shí)例化時(shí)作為參數(shù)指明這些類(lèi)型。各種程序設(shè)計(jì)語(yǔ)言和其編譯器、運(yùn)行環(huán)境對(duì)泛型的支持均不一樣。Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .NET 稱(chēng)之為泛型(generics);ML、Scala 和 Haskell 稱(chēng)之為參數(shù)多態(tài)(parametric polymorphism);C++ 和 D稱(chēng)之為模板。具有廣泛影響的1994年版的《Design Patterns》一書(shū)稱(chēng)之為參數(shù)化類(lèi)型(parameterized type)。

其中,C++的模版應(yīng)該是做的最完善的,不僅支持簡(jiǎn)單的模板替換,還可以處理一些簡(jiǎn)單的邏輯,經(jīng)過(guò)不斷的迭代,已經(jīng)形成了一種生成代碼的編程方式,因此也叫做模板元編程(Template metaprogramming)。當(dāng)然由于其和C++編程方式完全不一致,所以可讀性非常的差。而在Go的泛型設(shè)計(jì)中,為了保證泛型的簡(jiǎn)潔,Go并不支持模版元編程(心塞,還想試試在Go里面往往騷操作呢)。

Go的泛型

接下來(lái)就是Go泛型的使用介紹了,Go支持泛型函數(shù)和泛型類(lèi)型。

泛型函數(shù)

先來(lái)一個(gè)最簡(jiǎn)單的泛型函數(shù)

?
1
2
3
func ink19FirstGen[T any](t T) {
 fmt.Println(t)
}

這是一個(gè)非常簡(jiǎn)單的的函數(shù),就是使用fmt.Println打印輸入的參數(shù)。相比于以前的函數(shù),多了[T any]部分,這就是Go泛型的參數(shù)列表。

參數(shù)列表中的參數(shù)由兩部分組成,參數(shù)名和約束,其中T就是參數(shù),any為參數(shù)的約束。從表達(dá)上來(lái)說(shuō),和Go語(yǔ)言一貫的風(fēng)格相似,名在前,類(lèi)型在后。

在Go語(yǔ)言中,使用接口interface做為類(lèi)型的約束,其中any = interface{},即為無(wú)限制,但是以其說(shuō)是無(wú)限制,倒不如說(shuō)是完全限制,由于any里面沒(méi)有定義任何的方法,所以在函數(shù)里面也沒(méi)辦法調(diào)用t的任何方法。

這里有一個(gè)非常重要的問(wèn)題,就是相比較于C++的模板,Go會(huì)在定義函數(shù)的時(shí)候就對(duì)函數(shù)進(jìn)行解析。所以在函數(shù)中使用了的方法,一定要在約束的接口中出現(xiàn)。

?
1
2
3
4
5
6
7
type ink19Inf interface {
 Test()
}
 
func ink19FirstGen[T ink19Inf](t T) {
 t.Test()
}

和普通參數(shù)類(lèi)似的,如果是相同的約束,參數(shù)類(lèi)型也支持簡(jiǎn)化

?
1
2
3
func ink19FirstGen[T ,T2 ink19Inf](t T, t2 []T2) {
 t.Test()
}

泛型類(lèi)型

和C++中的模板類(lèi)類(lèi)似的,Go里面也有泛型類(lèi)型,它的定義也很簡(jiǎn)單

?
1
type ink19Vector[T any] []T

結(jié)構(gòu)相比與以前的類(lèi)型定義多了[T any]部分,這一部分的結(jié)構(gòu)和泛型函數(shù)那一部分類(lèi)似就不多介紹了。

對(duì)于泛型類(lèi)型,Go也可以定義相關(guān)的方法,譬如:

?
1
2
3
4
func (m *ink19Vector[T]) Push(v T) *ink19Vector[T] {
 *m = append(*m, v)
 return m
}

在泛型結(jié)構(gòu)體中,結(jié)構(gòu)體也可以定義自己的類(lèi)型的變量,形成鏈表

?
1
2
3
4
5
type List[T1, T2 any] struct {
 next *List[T1, T2]
 t1 T1
 t2 T2
}

PS:依據(jù)提案中的說(shuō)法,第二行的參數(shù)列表應(yīng)該和定義中的順序一致,以防止無(wú)限遞歸。但是在1.18beta1版本的實(shí)測(cè)中,順序不一致的寫(xiě)法并不會(huì)報(bào)錯(cuò)。

Go暫時(shí)不支持方法的泛型。

類(lèi)型集合

雖然通過(guò)接口限制類(lèi)型可以滿足絕大部分的要求,但是仍然有一些需求滿足不了,譬如運(yùn)算符。假如我們有一個(gè)函數(shù),可以傳入任意可比較的參數(shù),然后返回較小的那一個(gè)。很自然的,我們可以寫(xiě)下如下的代碼:

?
1
2
3
4
5
6
func whoismin[T any](a, b T) T {
  if a < b {
    return a
  }
  return b
}

但是,很遺憾的,由于我們對(duì)T的約束是any。所以其實(shí)來(lái)說(shuō),我們沒(méi)辦法對(duì)a和b做任何的操作,對(duì)比也是。所以在這里,我們會(huì)收到報(bào)錯(cuò)

?
1
invalid operation: cannot compare a < b (operator < not defined on T)

為了解決這一問(wèn)題,提案中提出了類(lèi)型集合的概念。

對(duì)于一個(gè)類(lèi)型,認(rèn)為它代表的類(lèi)型集合就是只包含這個(gè)類(lèi)型的集合,即對(duì)于類(lèi)型M來(lái)說(shuō),其代表的類(lèi)型集合為{M}。而對(duì)于接口來(lái)說(shuō),其對(duì)應(yīng)的類(lèi)型集合是無(wú)限的,只要一個(gè)類(lèi)型滿足接口的所有方法簽名,那么這個(gè)類(lèi)型就是屬于這個(gè)接口的類(lèi)型集合中。其實(shí)很容易理解類(lèi)型集合就是那個(gè)識(shí)別符可以代表的類(lèi)型的集合。

考慮集合的操作,對(duì)于下面這個(gè)例子

?
1
2
3
4
5
6
7
8
9
10
11
12
type ink19Inf1 interface {
 What1()
}
 
type ink19Inf2 interface {
 What2()
}
 
type ink19Inf3 interface {
 What1()
 What2()
}

假設(shè)ink19Inf1的類(lèi)型集合為A,ink19Inf2的為B,ink19Inf3的為C。那么很容得到C=A?B。即C為A和B的交集。當(dāng)然只有交集是不行的,后面還有說(shuō)明實(shí)現(xiàn)并集。

為了進(jìn)一步的說(shuō)明類(lèi)型集合,我們先來(lái)回憶一下接口的定義,對(duì)于之前的接口來(lái)說(shuō),接口的元素一共有兩種:方法簽名和其他接口。

?
1
2
3
4
5
6
7
8
type ink19Inf1 interface {
 What()
}
 
type ink19Inf2 interface {
 ink19Inf1
 It()
}

比如ink19Inf2中的第一個(gè)元素就是其他接口,第二個(gè)元素是其他簽名。但是僅僅只是有這兩種元素,對(duì)于泛型約束來(lái)說(shuō)是完全不夠的。為此,提案中加入了另外三種不同的元素,需要注意的是,如果一個(gè)接口加入了這額外三種元素,那么這個(gè)接口就不能再作為普通的接口使用了,只能用作泛型。

第一個(gè)增加的是類(lèi)型元素。以前的接口是不能用類(lèi)型作為接口的,但是在作為約束中可以這樣操作。作為元素的時(shí)候就是提供了一個(gè)只包含自己本身的類(lèi)型作為元素的類(lèi)型集合。

第二個(gè)是增加了近似約束元素,寫(xiě)法是在類(lèi)型前面增加~符號(hào),如

?
1
2
3
type ink19Inf1 interface {
 ~int
}

這一個(gè)元素的意義是為接口提供了一個(gè)所有以int為底層類(lèi)型的集合。所以被~修飾的類(lèi)型也應(yīng)該是一個(gè)底層類(lèi)型,不然提供的集合就是空集,沒(méi)有任何意義。具體的區(qū)別可以看下面的這個(gè)例子。

?
1
2
3
4
5
6
7
8
9
type ink19Inf3 interface {
 int
}
 
type ink19Inf4 interface {
 ~int
}
 
type MyInt int

首先我們定義了兩個(gè)接口,第一個(gè)接口使用的是額外的第一種元素, 因此它的類(lèi)型集合只包含了int。另一個(gè)使用了第二種元素,它的類(lèi)型集合包含了所有以int為底層類(lèi)型的類(lèi)型。然后我們定義了一個(gè)MyInt類(lèi)型,它是以int為底層類(lèi)型的類(lèi)型。需要注意的是,在Go中MyInt和int是兩種不同的類(lèi)型。最后我們寫(xiě)兩個(gè)方法來(lái)分別使用兩個(gè)接口為約束。

?
1
2
3
4
5
6
7
8
9
10
11
func ink19Print1[T ink19Inf3](t T) {
 fmt.Println(t)
}
 
func ink19Print2[T ink19Inf4](t T) {
 fmt.Println(t)
}
 
var data MyInt = 1
ink19Print1(data)  // 錯(cuò)誤
ink19Print2(data)

第三個(gè)元素是聯(lián)合約束。使用方法如下

?
1
2
3
type ink19Inf5 interface {
 int | float32 | bool | ~string | ink19Inf3
}

使用方法非常簡(jiǎn)單,就是將并集的元素一個(gè)一個(gè)使用|連接就就好了。需要注意的是聯(lián)合約束的元素只支持類(lèi)型,近視約束和其他只包含以上三種額外元素的接口(即,不支持包含方法簽名的接口)。

回到之前的問(wèn)題,對(duì)于需要使用操作符的情況,有了以上的工具后就可以解決了。

縱觀整個(gè)Go語(yǔ)言,由于并不支持操作符,所以有操作符(除了==和!=)的其實(shí)只有有限的幾種類(lèi)型,譬如:int,float32,string等等。

所以對(duì)于需要使用比較運(yùn)算符的約束的時(shí)候,可以使用如下的一個(gè)約束接口:

?
1
2
3
4
5
6
type Ordered interface {
 ~int | ~int8 | ~int16 | ~int32 | ~int64 |
  ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
  ~float32 | ~float64 |
  ~string
}

為了方便使用,Go標(biāo)準(zhǔn)庫(kù)里面提供了一個(gè)constraints來(lái)提供相關(guān)的約束。

上面提到,對(duì)于除了==和!=以外的操作符可以通過(guò)對(duì)所有的類(lèi)型進(jìn)行枚舉來(lái)實(shí)現(xiàn)。但是對(duì)于這兩個(gè)操作符,用戶(hù)自定義的類(lèi)型也會(huì)有這兩個(gè)操作符,沒(méi)辦法枚舉實(shí)現(xiàn)。官方給出的方法是通過(guò)使用一個(gè)一個(gè)內(nèi)建的約束comparable來(lái)完成操作。譬如

?
1
2
3
func IsSame[T comparable](a T, b T) bool {
 return a == b
}

和接口的差異

由于本人對(duì)于Go的接口使用并不多,所以如果有不足的地方請(qǐng)及時(shí)指正。

  • 實(shí)現(xiàn)方法上,泛型是編譯時(shí),接口是運(yùn)行時(shí);
  • 可以實(shí)現(xiàn)操作符的約束;
  • 返回的參數(shù)可以是特定的類(lèi)型,而接口只能返回固定的接口類(lèi)型;
  • 相比較于接口,泛型的約束可以有更多的操作。

總結(jié)

以上就是Go語(yǔ)言泛型的使用。總的來(lái)說(shuō),比較完整,實(shí)現(xiàn)了大部分的功能,相比于接口,有一定的差異。從體驗(yàn)上來(lái)說(shuō)有較高的提升,但是其缺點(diǎn)也非常的多。首先是其后面提出的三種元素,它將接口和類(lèi)型限制隔離開(kāi)了,這是一個(gè)特別奇葩的操作,感覺(jué)不符合Go語(yǔ)言的簡(jiǎn)潔實(shí)現(xiàn)。添加的三種元素中,我們主要來(lái)看第三種,聯(lián)合。代碼在分析的時(shí)候會(huì)對(duì)每一個(gè)元素測(cè)試,看看能不能通過(guò)編譯。所以從集合的角度上來(lái)看,如果我們把一個(gè)類(lèi)型可以進(jìn)行的操作可做是一個(gè)集合,那么這一個(gè)聯(lián)合就是在一個(gè)限定的類(lèi)型集合里面(枚舉出的)對(duì)每一個(gè)類(lèi)型的操作集合進(jìn)行一個(gè)交集操作。回到原來(lái),其實(shí)出現(xiàn)這個(gè)語(yǔ)法特性的最大原因就是Go語(yǔ)言不支持操作符重載,所以沒(méi)辦法對(duì)操作符進(jìn)行枚舉,那為什么不直接在這個(gè)版本實(shí)現(xiàn)操作符重載呢?或者直接不考慮這一部分,讓傳入的結(jié)構(gòu)體只能使用方法,不能使用操作符。并且,即使加入了這三種元素,還是有兩種操作符!=和==無(wú)法使用現(xiàn)在有的實(shí)現(xiàn),只能使用一個(gè)內(nèi)建的符號(hào)來(lái)代表這一類(lèi)的方法,個(gè)人感覺(jué)非常丑陋。

到此這篇關(guān)于淺談Go1.18中的泛型編程的文章就介紹到這了,更多相關(guān)Go  泛型編程內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://www.cnblogs.com/ink19/p/go_generic_programming.html

延伸 · 閱讀

精彩推薦
  • Golanggolang如何使用struct的tag屬性的詳細(xì)介紹

    golang如何使用struct的tag屬性的詳細(xì)介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細(xì)介紹,從例子說(shuō)起,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看...

    Go語(yǔ)言中文網(wǎng)11352020-05-21
  • GolangGolang通脈之?dāng)?shù)據(jù)類(lèi)型詳情

    Golang通脈之?dāng)?shù)據(jù)類(lèi)型詳情

    這篇文章主要介紹了Golang通脈之?dāng)?shù)據(jù)類(lèi)型,在編程語(yǔ)言中標(biāo)識(shí)符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語(yǔ)言中標(biāo)識(shí)符允許由...

    4272021-11-24
  • Golanggo語(yǔ)言制作端口掃描器

    go語(yǔ)言制作端口掃描器

    本文給大家分享的是使用go語(yǔ)言編寫(xiě)的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang 通過(guò)ssh代理連接mysql的操作

    golang 通過(guò)ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過(guò)ssh代理連接mysql的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧...

    a165861639710342021-03-08
  • Golanggolang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,...

    helight2992020-05-14
  • Golanggo日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    go日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號(hào)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧...

    SmallQinYan12302021-02-02
  • GolangGolang中Bit數(shù)組的實(shí)現(xiàn)方式

    Golang中Bit數(shù)組的實(shí)現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧...

    天易獨(dú)尊11682021-06-09
主站蜘蛛池模板: 亚洲香蕉伊在人在线观看9 亚洲系列国产系列 | 欧美久久影院 | 久久99热在线观看7 久久99精品涩AV毛片观看 | 欧美调教打屁股spank视频 | 国产精品激情综合久久 | 欧美在线成人免费国产 | 好男人影视社区www在线观看 | 久久精品熟女亚洲AV国产 | 日韩亚洲国产欧美精品 | 国产一区二区三区四卡 | 爱色v | 亚欧成人一区二区 | 国产精品全国探花在线观看 | 青久草视频 | 美女天天色 | 好性20岁| 国产一卡2卡3卡四卡精品网 | 狠狠色成人综合 | 免费在线视频一区 | 国产精品一区二区久久 | 日本动漫黄网站在线观看 | 成人在线视频在线观看 | 日韩精品久久不卡中文字幕 | 色综合久久天天综合观看 | 16男男gaygays | 日本视频免费在线观看 | 性欧美4khdxxxx | 91精品国产综合久久消防器材 | 精品一区二区三区在线成人 | 国产一线天 | 动漫美女强行被吸乳做羞羞事 | 羞羞污视频 | 国产成人亚洲综合91精品555 | 国产精品怡红院永久免费 | 色综合天天综合网国产人 | 波多洁野衣一二区三区 | 国产一区二区免费在线 | 色综合国产 | 丝袜捆绑调教视频免费区 | 亚洲国产精品线在线观看 | 亚洲 欧美 中文 日韩 另类 |