亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

目錄
什麼是泛型
go 中的泛型
形參、實參、型別參數(shù)、型別實參、實例化
類型集合、介面
基礎接口類型
接口組合
通用接口
類型推導
泛型的使用
內置容器類型
通用的結構體
通用的函數(shù)
總結
首頁 後端開發(fā) Golang 深入了解golang中的泛型(Generic)

深入了解golang中的泛型(Generic)

Apr 11, 2023 pm 07:20 PM
go 後端

這篇文章帶給大家的內容是介紹深入理解golang中的泛型?泛型怎麼使用?有一定的參考價值,有需要的朋友可以參考一下,希望對你們有幫助。

深入了解golang中的泛型(Generic)

什麼是泛型

#泛型(Generic)是一種程式設計技術。在強型別語言中, 允許編寫程式碼時使用以後才指定的型別, 在實例化時指定對應的型別。

在泛型中,可以使用型別參數(shù)來取代特定的資料型別。這些類型參數(shù)可以在類別、方法或介面中聲明,並且可以在這些聲明中使用。使用泛型的程式碼可以在運行時指定實際的類型參數(shù), 這使得程式碼可以適用於多種不同類型的資料。

泛型可以提高程式碼的可讀性、可維護性和可重複使用性。它可以減少程式碼的冗餘程度, 並且可以提供更好的類型安全性和編譯時類型檢查。

我們透過一個具體的例子來解釋為什麼泛型可以減少程式碼的冗餘:

提供一個函數(shù), 傳回a, b 的最小值, 我們需要每一種特定的資料類型「int, float...」寫一個函數(shù); 或使用interface{}「需要對參數(shù)進行類型斷言, 對運行性能有影響, 且無法約束傳入的參數(shù)」

func minInt(a, b int) int {
    if a > b {
        return b
    }
    return a
}

func minFloat(a, b float64) float64 {
    if a > b {
        return b
    }
    return a
}

func minItf(a, b interface{}) interface{} {
    switch a.(type) {
    case int:
        switch b.(type) {
        case int:
            if a.(int) > b.(int) {
                return b
            }
            return a
        }
    case float64:
        switch b.(type) {
        case float64:
            if a.(float64) > b.(float64) {
                return b
            }
            return a
        }
    }
    return nil
}

從上面的方法我們可以看出, minInt 和minFloat 除了參數(shù)與傳回結果的型別不同之外, 其餘程式碼皆相同。那有沒有一種方式可以不指定特定的型別, 在函數(shù)呼叫的時候再確定傳入的型別?這裡就引入一個概念叫泛型, 可以簡單理解為寬泛的類型或未指定具體型別。透過引入泛型, 我們就不需要再指定具體的資料型別, min 函數(shù)就可以使用下面的方式:

// T 為類型參數(shù), 在調用時確定參數(shù)的具體值, 可以為 int, 也可以為 float64;它與 a, b 一樣也是參數(shù), 需要調用時傳入具體的值;不同的是,T 為類型參數(shù),值為具體的類型, a,b 為函數(shù)參數(shù),值為具體類型對應的值
func minIntAndFloat64[T int | float64](a, b T) T { 
    if a < b {
        return a
    }
    return b
}

minIntAndFloat64[int](1, 2) // 實例化/調用時指定具體的類型

go 中的泛型

# #go 在1.8 版本中才引進了泛型。如果你的 go 版本低於 1.8, 那是無法使用泛型的。本文中的程式碼使用的版本為 1.9。在 1.8 版本中, 為支援泛型, 做了大量的改動。

    在函數(shù)和型別宣告中引入了型別參數(shù)
  • 可以透過介面定義型別的集合, 包含沒有方法的型別
  • 型別推導, 部分場景中會對型別參數(shù)進行推導, 可以在呼叫函數(shù)時不指定型別參數(shù)的值

形參、實參、型別參數(shù)、型別實參、實例化

先看一個普通的

add 函數(shù)。 add 為函數(shù)名稱, x, y 為形參, (x,y int)為參數(shù)清單。發(fā)生函數(shù)呼叫時, add(2, 3) 2, 3 為實參。

類比到泛型, 我們需要一個型別參數(shù), 當發(fā)生函數(shù)呼叫時傳入對應的型別實參, 帶有型別參數(shù)的函數(shù)叫做泛型函數(shù)。 深入了解golang中的泛型(Generic)[T int | int64] 為型別參數(shù)清單, T 為型別參數(shù), int | int64 為型別集合/型別約束。當發(fā)生函數(shù)呼叫時 add[int](2,3),int 即為型別實參, 這一呼叫我們也叫做實例化, 即確定型別實參。

深入了解golang中的泛型(Generic)

在結構體宣告時, 也可以指定型別參數(shù)。

MyStruct[T] 是一個泛型結構體, 可以為泛型結構體定義方法。

深入了解golang中的泛型(Generic)

類型集合、介面

在基礎類型, uint8 表示 0~255 的集合。那麼對於型別參數(shù), 也需要像基礎型別一樣, 定義型別的集合。在上面的例子中

int | string就是類型的集合。那麼如何對類型的集合進行複用呢?這裡就使用了接口來定義。下面就是一個型別集合的定義。因此, 我們可以定義一個泛型函數(shù) add[T Signed](x, y T) T

深入了解golang中的泛型(Generic)

#在go 1.8 之前, 介面的定義是方法的集合, 即實現(xiàn)了介面對應的方法, 就可以轉換為對應的介面。在下面的範例中,

MyInt 類型實作了 Add 方法, 因此可以轉換為 MyInterface。

type MyInterface interface {
    Add(x, y int) int
}

type MyInt int

func (mi myInt) Add(x, y int) int {
    return x + y
}

func main() {
    var mi MyInterface = myInt(1)
    fmt.Println(mi.Add(1, 2))
}

如果我們換個角度來思考一下,

MyInterface 可以看作一個型別集合, 即包含了所有實作 add 方法的型別。那麼, MyInterface 就可以當作型別集合使用。例如, 我們可以定義如下泛型函數(shù)。

func I[T MyInterface](x, y int, i T) int {
    return i.Add(x, y)
}

在泛型中, 我們的類型集合不僅僅是實現(xiàn)接口中定義方法的類型, 還需要包含基礎的類型。因此, 我們可以對接口的定義進行延伸, 使其支持基礎類型。為了保證向前兼容, 我們需要對接口類型進行分類:

基礎接口類型

只包含方法的集合, 既可以當作類型集合, 又可以作為數(shù)據類型進行聲明。如下面的 MyInterface。還有一個特殊的接口類型 interface{}, 它可以用來表示任意類型, 即所有的類型都實現(xiàn)了它的空方法。在 1.8 之后可以使用 any 進行聲明。

type any = interface{}

type MyInterface interface {
    Add(x, y int) int
    String() string
    String() string  // 非法: String 不能重復聲明
    _(x int)         // 非法: 必須要有一個非空的名字
}

接口組合

可以通過接口組合的形式聲明新的接口, 從而盡可能的復用接口。從下面的例子可以看出, ReadWriterReaderWrite 的類型集合的交集。

type Reader interface {
        Read(p []byte) (n int, err error)
        Close() error
}

type Writer interface {
        Write(p []byte) (n int, err error)
        Close() error
}

// ReadWriter&#39;s methods are Read, Write, and Close.
type ReadWriter interface {
        Reader  // includes methods of Reader in ReadWriter&#39;s method set
        Writer  // includes methods of Writer in ReadWriter&#39;s method set
}

通用接口

上面說的接口都必須要實現(xiàn)具體的方法, 但是類型集合中無法包含基礎的數(shù)據類型。如: int, float, string...。通過下面的定義, 可以用來表示包含基礎數(shù)據類型的類型集合。在 golang.org/x/exp/constraints 中定義了基礎數(shù)據類型的集合。我們可以看到 符號, 它表示包含潛在類型為 int | int8 | int16 | int32 | int64 的類型, | 表示取并集。Singed 就表示所有類型為 int 的類型集合。

// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
     ~int | ~int8 | ~int16 | ~int32 | ~int64
}

type myInt int // 潛在類型為 int

func add[T constraints.Integer](x, y T) T {
        return x + y
}

func main() {
        var x, y myInt = 1, 2
        fmt.Println(add[myInt](x, y))
}

下面來看一些特殊的定義

// 潛在類型為 int, 并且實現(xiàn)了 String 方法的類型
type E interface {
    ~int
    String() string
}

type mInt int // 屬于 E 的類型集合
func (m mInt) String() string {
    return fmt.Sprintf("%v", m)
}

// 潛在類型必須是自己真實的類型
type F interface {
    ~int
    // ~mInt  invalid use of ~ (underlying type of mInt is int)
    // ~error illegal: error is an interface
}

// 基礎接口可以作為形參和類型參數(shù)類型, 通用類型只能作為類型參數(shù)類型, E 只能出現(xiàn)在類型參數(shù)中 [T E]
var x E                    // illegal: cannot use type E outside a type constraint: interface contains type constraints
var x interface{} = E(nil) // illegal: cannot use interface E in conversion (contains specific type constraints or is comparable)

類型推導

由于泛型使用了類型參數(shù), 因此在實例化泛型時我們需要指定類型實參。 看下面的 case, 我們在調用函數(shù)的時候并沒有指定類型實參, 這里是編譯器進行了類型推導, 推導出類型實參, 不需要顯性的傳入。

func add[T constraints.Integer](x, y T) T {
    return x + y
}

func main() {
    fmt.Println(add(1, 1)) // add[int](1,1)
}

有時候, 編譯器無法推導出具體類型。則需要指定類型, 或者更換寫法, 也許可以推斷出具體類型。

// 將切片中的值擴大
func Scale[E constraints.Integer](s []E, c E) []E {
    r := make([]E, len(s))
    for i, v := range s {
        r[i] = v * c
    }
    return r
}

func ScaleAndPrint(p Point) {
    r := Scale(p, 2)
    r.string() // 非法, Scale 返回的是 []int32
}

type Point []int32

func (p Point) string() {
    fmt.Println(p)
}

// 方法更新,這樣傳入的是 Point 返回的也是 Point
func Scale[T ~[]E, E constraints.Integer](s T, c E) T {
    r := make([]E, len(s))
    for i, v := range s {
        r[i] = v * c
    }
    return r
}

泛型的使用

go 是在 1.8 版本中開始引入泛型的。下面主要介紹一下什么時候使用泛型:

內置容器類型

在 go 中, 提供以下容器類型:map, slice, channel。當我們用到容器類型時, 且邏輯與容器具體的類型無關, 這個時候可以考慮泛型。這樣我們可以在調用時指定具體的類型實參, 從而避免了類型斷言。例如,下面的例子, 返回 map 中的 key。

// comparable 是一個內置類型, 只能用于對類型參數(shù)的約束。在 map 中, key 必須是可比較類型。
func GetKeys[K comparable, V any](m map[K]V) []K {
    res := make([]K, 0, len(m))
    for k := range m {
        res = append(res, k)
    }
    return res
}

通用的結構體

對于一些通用的結構體, 我們應該使用泛型。例如, 棧、隊列、樹結構。這些都是比較通用的結構體, 且邏輯都與具體的類型無關, 因此需要使用泛型。下面是一個棧的例子:

type Stack[T any] []T

func (s *Stack[T]) Push(item T) {
    *s = append(*s, item)
}

func (s *Stack[T]) Pop() T {
    if len(*s) == 0 {
        panic("can not pop item in emply stack")
    }
    lastIndex := len(*s) - 1
    item := (*s)[lastIndex]
    *s = (*s)[:lastIndex]
    return item
}

func main() {
    var s Stack[int]
    s.Push(9)
    fmt.Println(s.Pop())
    s.Push(9)
    s.Push(8)
    fmt.Println(s.Pop(), s.Pop())
}

通用的函數(shù)

有些類型會實現(xiàn)相同的方法, 但是對于這些類型的處理邏輯又與具體類型的實現(xiàn)無關。例如: 兩個數(shù)比大小, 只要實現(xiàn) Ordered 接口即可進行大小比較:

func Min[T constraints.Ordered](x, y T) T {
    if x < y {
        return x
    }

    return y
}

func main() {
    fmt.Println(Min(5, 6))
    fmt.Println(Min(6.6, 9.9))
}

總結

go 在引入泛型算是一次較大的改動。我們只有弄清楚類型參數(shù)、類型約束、類型集合、基礎接口、通用接口、泛型函數(shù)、泛型類型、泛型接口等概念, 才能不會困惑。核心改動點還是引入了類型參數(shù), 使用接口來定義類型集合。

當然,也不能為了使用泛型而使用泛型。還是要具體的 case 具體來分析。 簡單的指導原則就是, 當你發(fā)現(xiàn)你的代碼除了類型不同外, 其余代碼邏輯都相同; 或者你寫了許多重復代碼, 僅僅是為了支持不同類型; 那么你可以考慮使用泛型。

推薦學習:Golang教程

以上是深入了解golang中的泛型(Generic)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發(fā)現(xiàn)涉嫌抄襲或侵權的內容,請聯(lián)絡admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Laravel 教程
1597
29
PHP教程
1488
72
Switch語句如何運行? Switch語句如何運行? Jul 30, 2025 am 05:11 AM

Go的switch語句默認不會貫穿執(zhí)行,匹配到第一個條件後自動退出。 1.switch以關鍵字開始並可帶一個值或不帶值;2.case按順序從上到下匹配,僅運行第一個匹配項;3.可通過逗號列出多個條件來匹配同一case;4.不需要手動添加break,但可用fallthrough強制貫穿;5.default用於未匹配到的情況,通常放最後。

使用上下文軟件包進行取消和超時 使用上下文軟件包進行取消和超時 Jul 29, 2025 am 04:08 AM

USECONTEXTTOPROPAGATECELLATION ANDDEADEADLINESACROSSGOROUTINES,ENABLINGCOOPERATIVECELLATIONININHTTPSERVERS,背景任務,andChainedCalls.2.withContext.withContext.withCancel(),CreatseAcancellableBableBablebableBableBableBablebableContExtandAndCandExtandCallCallCancelLcancel()

建立表演者為第三方API的客戶 建立表演者為第三方API的客戶 Jul 30, 2025 am 01:09 AM

使用專用且配置合理的HTTP客戶端,設置超時和連接池以提升性能和資源利用率;2.實現(xiàn)帶指數(shù)退避和抖動的重試機制,僅對5xx、網絡錯誤和429狀態(tài)碼重試,並遵守Retry-After頭;3.對靜態(tài)數(shù)據如用戶信息使用緩存(如sync.Map或Redis),設置合理TTL,避免重複請求;4.使用信號量或rate.Limiter限制並發(fā)和請求速率,防止被限流或封禁;5.將API封裝為接口,便於測試、mock和添加日誌、追蹤等中間件;6.通過結構化日誌和指標監(jiān)控請求時長、錯誤率、狀態(tài)碼和重試次數(shù),結合Op

如何在Go中正確複製切片 如何在Go中正確複製切片 Jul 30, 2025 am 01:28 AM

要正確複製Go中的切片,必須創(chuàng)建新的底層數(shù)組,而不是直接賦值;1.使用make和copy函數(shù):dst:=make([]T,len(src));copy(dst,src);2.使用append與nil切片:dst:=append([]T(nil),src...);這兩種方法都能實現(xiàn)元素級別的複制,避免共享底層數(shù)組,確保修改互不影響,而直接賦值dst=src會導致兩者引用同一數(shù)組,不屬於真正複製。

如何將template.parsefs與GO嵌入? 如何將template.parsefs與GO嵌入? Jul 30, 2025 am 12:35 AM

使用template.ParseFS與embed包可將HTML模板編譯進二進製文件。 1.導入embed包並用//go:embedtemplates/.html將模板文件嵌入embed.FS變量;2.調用template.Must(template.ParseFS(templateFS,"templates/.html"))解析所有匹配的模板文件;3.在HTTP處理器中通過tmpl.ExecuteTemplate(w,"home.html",nil)渲染指定

符文是什麼? 符文是什麼? Jul 31, 2025 am 02:15 AM

Aruneingoisaunicodecodepointrepointreporentedasanint32,使用了tocortloctlyhandhandlenternationCharacters; 1. userunesInesinSteadofbyTestoavoidSplittingMulti-bydeunicodecharacters; 2. 2. loopoverstringswithrangetogetrogetogetogetrogeTringsWithRangetogetrounes,notbyters; 3.converteranemantermaneflymantofelymanteranemantermanterantoflyman [] []

與時間和日期一起工作 與時間和日期一起工作 Jul 30, 2025 am 02:51 AM

Go使用time.Time結構體處理日期和時間,1.格式化和解析使用參考時間“2006-01-0215:04:05”對應“MonJan215:04:05MST2006”,2.創(chuàng)建日期使用time.Date(year,month,day,hour,min,sec,nsec,loc)並指定時區(qū)如time.UTC,3.時區(qū)處理通過time.LoadLocation加載位置並用time.ParseInLocation解析帶時區(qū)的時間,4.時間運算使用Add、AddDate和Sub方法進行加減和計算間隔,

如何在GO中導入本地軟件包? 如何在GO中導入本地軟件包? Jul 30, 2025 am 04:47 AM

要正確導入本地包,需使用Go模塊並遵循目錄結構與導入路徑匹配原則。 1.使用gomodinit初始化模塊,如gomodinitexample.com/myproject;2.將本地包放在子目錄中,如mypkg/utils.go,包聲明為packagemypkg;3.在main.go中通過完整模塊路徑導入,如import"example.com/myproject/mypkg";4.避免相對導入、路徑不匹配或命名衝突;5.對於模塊外的包可使用replace指令。只要確保模塊初始化

See all articles