生成模板代碼實現Golang泛型
Golang是強類型靜態語言,暫不支持泛型。那麼如何定義通用類型的數據結構和算法呢?使用interface{}
不是好的解決方案,接口需要轉換同時會丟失靜態類型的優勢。本文我們介紹模板代碼結合代碼生成器,實現編譯時檢查、安全的、高性能的泛型編程。
1. 實現通用Set數據結構
如何實現一個數據結構(算法也一樣)支持常用類型,生成特定類型實現,可以輕鬆重用。
使用genny,非常簡單幾個步驟:
- 導入
genny/generic
- 定義類型爲通用類型
generic.Type
,例如,命令爲Item
或Type
- 在代碼中使用這些通用類型
- 運行
genny
生成類型實現
下面實現比較簡單的Set數據結構,定義ItemSet泛型Set,同時定義了Add和Clean方法:
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Item=string,int"
package set
import "github.com/cheekybits/genny/generic"
// Item the type of the Set
type Item generic.Type
// ItemSet the set of Items
type ItemSet struct {
items map[Item]bool
}
func (s *ItemSet)Add(t Item) ItemSet {
if s.items == nil {
s.items = make(map[Item]bool)
}
_, ok := s.items[t]
if !ok {
s.items[t] = true
}
return *s
}
func (s *ItemSet) Clean(){
s.items = make(map[Item]bool)
}
當然需要先使用go get -u github.com/cheekybits/genny
命令安裝genny,編譯通過後。運行命令:
genny -in set.go -out gen-set.go gen "Item=string,int"
命令執行之後,如果輸入文件爲set.go
,則會生成gen-set.go
:
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
// Package Set creates a StringSet data structure for the string type
package set
// StringSet the set of Strings
type StringSet struct {
items map[string]bool
}
// Add adds a new element to the Set. Returns the Set.
func (s *StringSet) Add(t string) StringSet {
s.items[t] = true
return *s
}
// Clear removes all elements from the Set
func (s *StringSet) Clear() {
(*s).items = make(map[string]bool)
}
// Delete removes the string from the Set and returns Has(string)
func (s *StringSet) Delete(item string) bool {
ret := (*s).Has(item)
if ret {
delete((*s).items, item)
}
return ret
}
// Has returns true if the Set contains the string
func (s *StringSet) Has(item string) bool {
return (*s).items[item]
}
// Strings returns the string(s) stored
func (s *StringSet) Strings() []string {
items := []string{}
for i := range s.items {
items = append(items, i)
}
return items
}
// Package Set creates a IntSet data structure for the int type
// IntSet the set of Ints
type IntSet struct {
items map[int]bool
}
// Add adds a new element to the Set. Returns the Set.
func (s *IntSet) Add(t int) IntSet {
s.items[t] = true
return *s
}
// Clear removes all elements from the Set
func (s *IntSet) Clear() {
(*s).items = make(map[int]bool)
}
// Delete removes the int from the Set and returns Has(int)
func (s *IntSet) Delete(item int) bool {
ret := (*s).Has(item)
if ret {
delete((*s).items, item)
}
return ret
}
// Has returns true if the Set contains the int
func (s *IntSet) Has(item int) bool {
return (*s).items[item]
}
// Ints returns the int(s) stored
func (s *IntSet) Ints() []int {
items := []int{}
for i := range s.items {
items = append(items, i)
}
return items
}
我們看到genny
幫我們從ItemSet **
生成了StringSet
和IntSet
結構,因爲Item=string,int
。
同時也生成對應結構體方法,如果你想增加其他類型,只要編輯命令重新運行。
2. 使用 go generate
我們也可以通過在文件的頂部,包文檔註釋的下面增加go generate
註釋:
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Item=string,int"
命令指定使用genny
,同時需要翻譯Item爲string和int。接着直接在源文件目錄下運行下面命令:
go generate
同樣會生成gen-set.go
文件,包括string和int類型結構體。
重要:上面通用實現的數據結構可以像
go generate
生成的具體實現一樣,進行編譯和測試,因此可以針對通用或特定類型的實現運行測試。
3. 總結
本文介紹了利用genny
和go generate
生成代碼,輔助實現泛型數據結構和算法。