阅读 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 版本 ,命令式编程语言
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
《10%创业家》
[美] 帕特里克•J.麦金尼斯 / 李文远 / 广东人民出版社 / 2017-4 / 45.00
还在打工和创业之间苦苦挣扎吗?麦金尼斯用亲身经历告诉你,不用辞职,只需投入10%的时间和资源,就能获得100%的财务自由。你不需要雄厚的资本,也不必占用工作时间,只要准确掌握本书所授的方法,就能立即开始创业。 麦金尼斯是世界银行风投顾问,同时也是一名10%创业家。在本书中,他结合自身的创业咨询经历,为读者讲解了移动互联时代的5种创业模式,还提供了创业基因测试、10%创业计划、自传模板等个性化......一起来看看 《《10%创业家》》 这本书的介绍吧!