Golang 1.x版本泛型编程

栏目: Go · 发布时间: 6年前

阅读 2

Golang 1.x版本泛型编程

Go是一门天生为服务器程序设计的简洁的语言,因此 Go 的设计原则聚焦在可扩展性、可读性和并发性,而多态性并不是这门语言的设计初衷,因此就被放在了一边。虽然在2.0版本之前还没有泛型的支持,但是Go自带的一些语言特性可以满足一些类似“泛型”的要求,比如内置类型:

  1. array

  2. slice

  3. map

  4. 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版本泛型编程


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Text Processing in Python

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》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换