1. 泛型簡介
今天是2021年12月16日, 前天, 也就是14號, Golang官方發布了go1.18的Beta版本, 其中最重磅的更新, 當屬萬眾矚目的泛型(Generics)
泛型是什么? 有些小伙伴也許并不清楚其中的概念, 實際上, 泛型就是和C++中模板等位的產品
例如:
vector<int> v;
v.push_back(1);
中的int就指定了v中存盤元素的型別為int, 也可以替換成其他你想要存盤的型別
自定義模板, 只需要:
template<class T>
class Vector{
int mLen;
T* data;
};
又比如Java中也有泛型的概念:
class Vector<T>{
public void push_back(T val){
//implement...
}
}
如今, Go也有了自己的泛型, 而且個人認為比起其他語言, Go的泛型更加的好用(畢竟Go的泛型最近幾年才開發, 吸取了很多高級語言的精華)
看起來就像這樣:
func MyPrintln[T any](a T) {
fmt.Println(a)
}
func main() {
MyPrintln(1)
MyPrintln("小王")
MyPrintln([]int{3, 2, 1})
//運行結果:
//1
//小王
//[3 2 1]
}
2. 泛型語法詳解
下面開始詳細介紹泛型的語法
MyType[T1 constraint1 | constraint2, T2 constraint3...] ...
泛型的語法非常簡單, 就類似于上面這樣, 其中:
- MyType可以是函式名, 結構體名, 型別名…
- T1, T2…是泛型名, 可以隨便取
- constraint的意思是約束, 也是泛型中最重要的概念, 接下來會詳解constraint
- 使用 | 可以分隔多個constraint, T滿足其中之一即可(如T1可以是constraint1和constraint2中的任何一個)
2.1 Constraint(約束)是什么
約束的意思是限定范圍, constraint的作用就是限定范圍, 將T限定在某種范圍內
而常用的范圍, 我們自然會想到的有:
- any(interface{}, 任何型別都能接收, 多方便啊!)
- Interger(所有int, 多方便啊, int64 int32…一網打盡)
- Float(同上)
- comparable(所有可以比較的型別, 我們可以給所有可以比較的型別定制一些方法)
- …
這些約束, 不是被官方定義為內置型別, 就是被涵蓋在了constraints包內!!!
下面是builtin.go的部分官方原始碼:
// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}
// comparable is an interface that is implemented by all comparable types
// (booleans, numbers, strings, pointers, channels, interfaces,
// arrays of comparable types, structs whose fields are all comparable types).
// The comparable interface may only be used as a type parameter constraint,
// not as the type of a variable.
type comparable comparable
下面是constraints.go的部分官方原始碼:
// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
Signed | Unsigned
}
// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
~float32 | ~float64
}
//......
可以看到, 官方還是非常貼心的, 很多輪子已經幫我們造好了
而通過觀察constraints包和閱讀官方檔案, 我也掌握了如何自定義約束
2.2 自定義constraint(約束)
下面是constraints包中的官方原始碼:
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
Signed約束就是這樣被寫出來的, 其中需要我們get的點有如下幾個:
- 使用interface{}就可以自定義約束
- 使用 | 就可以在該約束中包含不同的型別, 例如int, int8, int64均滿足Signed約束
- 你可能會有疑問, ~是什么??? int我認識, ~int我可不認識呀??? 沒關系, 實際上~非常簡單, 它的意思就是模糊匹配, 例如:
- type MyInt int64
- 此時 MyInt并不等同于int64型別(Go語言特性)
- 若我們使用int64來約束MyInt, 則Myint不滿足該約束
- 若我們使用~int64來約束MyInt, 則Myint滿足該約束(也就是說, ~int64只要求該型別的底層是int64, 也就是模糊匹配了)
- 官方為了魯棒性, 自然把所有的型別前面都加上了~
下面我們自定義一個約束
type My_64_Bits_Long_Num interface {
~int64 | ~float64
}
是不是很簡單?
3. 泛型綜合使用案例
3.1 自定義型別
這里自定義一個map
//map的key必須要可以比較, 也就是可以被 == 和 != 比較(用于處理哈希沖突)
type MyMap[K comparable, V constraints.Integer | constraints.Float] map[K]V
func main() {
m := make(MyMap[string, int])
m["表哥"] = 100
m["小張"] = 0
for k, v := range m{
fmt.Printf("key: %v, val: %v\n", k, v)
}
}
3.2 自定義結構體
這里以一個手寫鏈表(只能存盤整數)做示范
type MyIntergerNode[T constraints.Integer] struct {
Next *MyIntergerNode[T]//注意這里一定要加型別宣告(和C++一樣)
Data T
}
func main() {
head := &MyIntergerNode[int64]{Next: nil, Data: 1}
head.Next = &MyIntergerNode[int64]{Next: nil, Data: 2}
for p := head; p != nil; p = p.Next{
fmt.Printf("%d ", p.Data)
}
}
3.3 自定義函式
這里自定義一個比較64位元大小的型別的函式
//剛才自定義的約束
type My_64_Bits_Long_Num interface {
~int64 | ~float64
}
func MyCompare[T My_64_Bits_Long_Num](a, b T) bool {
return a < b
}
func main() {
var a int64 = 1
var b int64 = 8
//函式可以省略不寫引數型別(語法糖)
ans := MyCompare(a, b)
if ans{
fmt.Printf("%v小于%v", a, b)
}else{
fmt.Printf("%v大于%v", a, b)
}
}
4. 總結
爆肝一小時, 終于肝出了這篇文章
go的泛型等了很久, 有了泛型, go日后也可以制造更多強大的容器, 甚至slice, map都可以被重寫一份泛型版本(看官方意愿啦)
總之, 希望看完這篇文章的你, 能夠掌握go的泛型(明年2月Go1.18正式上線就可以應用到生產環境了)
如果學會了, 介意給個免費的贊嗎😁
感謝閱讀, 拜拜~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/382925.html
標籤:其他
