生成模板代码实现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
生成代码,辅助实现泛型数据结构和算法。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/107165041