12.泛型

? ? Go語言在v1.18版本添加。其中泛即通用的意思。

12.1 泛型函數(shù)

? ? 如果沒有泛型,同一種類型需要使用重載功能,但Go又不支持重載功能。如果寫一個簡單的加法計算函數(shù),我們只能定義出如下所示的代碼:

func AddInt(a, b int) int {
    return a + b
}

func AddFloat(a, b float64) float64 {
    return a + b
}

func JoinString(a, b string) string {
    return a + b
}

? ? 從上面代碼可以看出,每個函數(shù)的參數(shù)個數(shù)都一樣,僅僅參數(shù)的數(shù)據(jù)類型不一樣,如果還有類似的功能,僅僅是參數(shù)數(shù)據(jù)類型不一致,就還得增加函數(shù),從而導致代碼存在大量的重復。那么有沒有一種,可以將參數(shù)的數(shù)據(jù)類型再抽象一次來解決這樣的問題,我們來看看以下代碼

func Add(a,b T) T{
    return a+b
}

? ? 上面代碼中的T表示類型形參,即參數(shù)的數(shù)據(jù)類型是可變的,a,b T則表明a和b的類型要一致。即在運行時,T最終一定會確定是某一種數(shù)據(jù)類型。Go語言針對這一問題的解決方案是泛型。其語法格式如下所示:

func name[T,P](parameters-1 T,parameters-2 P) P{
    函數(shù)語句塊
}

? ? 示例代碼如下所示:

package main

import "fmt"

func Add[T int | float64 | string](a, b T) T {
    return a + b
}

func main() {
    fmt.Printf("int: %v,%[1]T\n", Add(4, 5))
    fmt.Printf("float: %v,%[1]T\n", Add(4.7, 5.8))
    fmt.Printf("string: %v,%[1]T\n", Add("Sur", "pass"))
}

? ? 運行結果如下所示:

int: 9,int
float: 10.5,float64
string: Surpass,string

? ? 通過以上代碼,可以看到代碼量大大減小,而且也非常簡潔。

  • T: 稱為類型形參,僅僅是一個類型占位符
  • int | float64 | string: 稱為類型約束,其中|表示或的意思
  • T int | float64 | string:稱為類型參數(shù)列表,存在多個時,使用逗號進行分隔,例如 [T int | string , P string | bool]
  • Add[T]:即泛型函數(shù)
  • Add[int]: 即類型實參,傳入int給泛型函數(shù)的過程稱為泛型函數(shù)實例化
  • Add[int](4,5) 可以簡寫為 Add(4,5),因為可以根據(jù)傳入的參數(shù)自動推斷出數(shù)據(jù)類型
  • 定義泛型函數(shù)時,函數(shù)名后面緊跟著類型參數(shù)列表。因此匿名函數(shù)不可以定義為泛型函數(shù),但可通過使用定義的類型開通T

12.2 類型約束

? ? 類型約束是一個接口。為支持泛型,Go v1.18對接口進行了語法擴展。用在泛型中,接口含義就是符合這些特征的類型集合。在Go語言中,內置了兩個約束:

  • any: 表示任意類型
  • comparable: 表示類型的值可以使用==!=進行比較
[T int ] 等價于[T interface{int}] // 表示T只能是int類型

type Constraint interface{
    int | string
}

[T int | string ] 、 [ T interface{int | string }] 和 [T Constraint]  三者等價,都表示類型只能是int或string類型

12.3 泛型類型

? ? 示例代碼如下所示:

package main

import "fmt"

// 接口從本質上講,就是一種類型約束,如果要使用接口類型,則要求其結構必須接口中所有的方法
type Runer interface {
    Run()
}

// 定義一種新的Map類型,要求Key的數(shù)據(jù)類型為int或string,而value為Runner類型
// [K string | int, V Runer] 對自定義類型的參數(shù)進行約束
type MyMap[K string | int, V Runer] map[K]V

type MyString string // 類型新定義

func (myString MyString) Run() {
    fmt.Printf("MyString:%#v - %[1]T\n", myString)
}

func main() {
    // var m MyMap[string, MyString] = make(MyMap[string, MyString])
    d := make(MyMap[string, MyString]) //相當于map[string]MyString{}
    fmt.Printf("d: %#v - %[1]T\n", d)
    d["name"] = "Surpass"
    fmt.Printf("d: %#v - %[1]T\n", d)
    d["name"].Run()
}

? ? 運行結果如下所示:

d: main.MyMap[string,main.MyString]{} - main.MyMap[string,main.MyString]
d: main.MyMap[string,main.MyString]{"name":"Surpass"} - main.MyMap[string,main.MyString]
MyString:"Surpass" - main.MyString

? ? 結合以上代碼,泛型是對函數(shù)的參數(shù)或返回值設置多個數(shù)據(jù)類型,比普通函數(shù)更靈活的設置參數(shù)類型和返回值類型,但卻又比不上空接口參數(shù)的開放自由。

? ? 既然泛型比不上空接口,那為什么還要引入泛型呢?我們知道空接口參數(shù)不受數(shù)據(jù)類型限制,但如果在調用過程中,函數(shù)傳入的參數(shù)是無法處理的數(shù)據(jù)類型,則很容易導致出現(xiàn)異常情況。而使用泛型時,既可以對函數(shù)的參數(shù)和返回值數(shù)據(jù)類型進行約束,也能保證傳入?yún)?shù)的多樣性。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 泛型歷史和概述 泛型發(fā)展 泛型程序最早出現(xiàn)1970年代的CLU和Ada語言中,后來被許多基于對象和面向對象的語言所...
    迷心迷閱讀 675評論 0 0
  • 參考C# 泛型 泛型與非泛型集合[http://www.itdecent.cn/p/e45e7573f928]一...
    合肥黑閱讀 835評論 0 2
  • 泛型 泛型(Generic Type)簡介 通常情況的類和函數(shù),我們只需要使用具體的類型即可:要么是基本類型,要么...
    Tenderness4閱讀 1,522評論 4 2
  • Go 泛型發(fā)展史與基本介紹 Go 1.18版本增加了對泛型的支持,泛型也是自 Go 語言開源以來所做的最大改變。 ...
    賈維斯Echo閱讀 250評論 0 0
  • 泛型(Generics) 泛型代碼允許你定義適用于任何類型的,符合你設置的要求的,靈活且可重用的 函數(shù)和類型。泛型...
    果啤閱讀 760評論 0 0

友情鏈接更多精彩內容