阅读 2
Golang 1.x版本泛型编程
Go是一门天生为服务器程序设计的简洁的语言,因此 Go 的设计原则聚焦在可扩展性、可读性和并发性,而多态性并不是这门语言的设计初衷,因此就被放在了一边。虽然在2.0版本之前还没有泛型的支持,但是Go自带的一些语言特性可以满足一些类似“泛型”的要求,比如内置类型:
-
array
-
slice
-
map
-
chan
这四种类型可以用任意类型的元素初始化,例如map[yourtype]bool就可以用来实现任意元素的集合。Go的一些内置函数也是通用的,比如len()既可以用于string, array, slice, 也可以用于求map的长度。
但是如果golang内置结构和函数不能满足需求,可以从以下几种方法来入手:
类型断言
当你想实现一个通用函数的时候,会考虑传入的参数是不是固定类型的,Go正好提供了interface{}类型,它可以代表任意类型。当你不确定用什么类型合适的时候,用它就对了。举个简单的例子:
type Container struct {
elem []interface{}
}
func (this *Container) Put(v interface{}) {
*this = append(*this, elem)
}
// 取出最后一个元素
func (this *Container) Get() interface{} {
ret := (*c)[len(*c)-1]
*c = (*c)[:len(*c)-1]
return ret
}
func assertExample() {
container := &Container{}
container.Put(1)
container.Put("Hello")
_, ok := container.Get().(int);!ok {
fmt.Println("Unable to read an int from container")
}
}复制代码
通过接口类型我们把细节完全隐藏了起来,但是我们也把运行时类型检查失败的风险留给了调用者,而且调用者每次都要写
_, ok := YourType.(type);!ok{}复制代码
这种类型断言,比较啰嗦。
反射机制
反射机制就是在运行时动态的调用对象的方法和属性,Python和 Java 语言都有实现,而Golang是通过reflect包来实现的。反射机制允许程序在编译时处理未知类型的对象,这听上去可以解决我们上面的问题,现在修改一下代码:
type Container struct {
elem reflect.Value
}
func NewContainer(t reflect.Type) *Container {
return &Container{
elem: reflect.MakeSlice(reflect.SliceOf(t), 0, 10),
}
}
func (this *Container) Put(v interface{}) {
if reflect.ValueOf(val).Type() != c.elem.Type().Elem() {
panic(fmt.Sprintf("Cannot set a %T into a slice of %s", val, c.elem.Type().Elem()))
}
c.elem = reflect.Append(c.elem, reflect.ValueOf(val))
}
func (this *Container) Get(regref interface{}) interface{} {
retref = c.elem.Index(c.elem.Len()-1)
c.elem = c.elem.Slice(0, c.elem.Len()-1)
return retref
}
func assertExample() {
a := 0.123456
nc := NewContainer(reflect.TypeOf(a))
nc.Put(a)
nc.Put(1.11)
nc.Put(2.22)
b := 0.0
c := nc.Get(&b)
fmt.Println(c)
d := nc.Get(&b)
fmt.Println(d)
}复制代码
可以看到使用反射的代码量要增加不少,而且要写各种reflect方法的前缀。这些对于有代码洁癖的人来说是个灾难,也会让 程序员 效率变慢,因为没有编译时的类型检查,会带来额外的运行开销。
使用接口
接口有个特点是只做定义不管细节实现,我们可以利用这一特性实现泛型,例如标准库中的sort包就是使用接口实现对任意容器元素 排序 的:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}复制代码
只要实现接口定义的这三种方法,即可对自定义的容器元素进行排序,具体例子可以参考sort包的文档。查看sort包的源码后可以发现代码非常简洁,但是缺点也很明显,使用者需要自己把接口方法重新实现一遍。
代码生成工具
代码生成的原理是先写一个mock类型,这个mock只做占位符使用,然后通过转换 工具 把这个占位符替换成具体类型,已经有很多人写过了,这里就不再多说。这样做的缺点是生成的文件比较大,依赖第三方工具和模板语法。
总之,Golang的泛型实现没有一个固定的方法,或者说一个放之四海而皆准的理想方法,程序设计达到一定规模后,总是需要在代码效率,编译器效率和运行效率之间做出一些取舍,因此我们需要知道实现泛型的不同方法,在适当的时候使用合适的那个就可以了。
文章首发于小米运维公众号,原文请戳 Golang 1.x版本泛型编程
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Crystal 编程语言正式迈入 1.0 版本
- Rust 1.26 版本发布,系统编程语言
- Rust 1.26 版本发布,系统编程语言
- Elixir 1.4.4 版本发布,函数式编程语言
- Dlang 2.080.0 版本发布,系统级编程语言
- Nim 发布 0.19 版本 ,命令式编程语言
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Text Processing in Python
David Mertz / Addison-Wesley Professional / 2003-6-12 / USD 54.99
Text Processing in Python describes techniques for manipulation of text using the Python programming language. At the broadest level, text processing is simply taking textual information and doing som......一起来看看 《Text Processing in Python》 这本书的介绍吧!