大家好,我是从不放鸽子的炸鱼。前段时间,关于Go语言泛型的讨论频频出现在各个微信群,并冲上了国内外各大文章的“头条”:根据信息汇总,Go泛型将在近几年发布,但总的来说,GoGenerics一拖再拖。好家伙,刚开始知道的时候考虑Go1.16的发布,后来推到Go1.17,现在推迟到Go1.18(2021年底)。看到表面的信息后,泛型“东西”突然火起来的原因之一是Go官方11岁[1]的博文点燃了它。同时,在最近的GopherCon2020大会上,RobertGriesemer分享了Typing[Generic]Go。更正式地说,Go泛型更面向大众,也表明官方认为它已经到了一个新的阶段,进入了最后的实施阶段。废话不多说,既然官方已经在摩拳擦掌,我们的学习之路也得跟上,所以本文就介绍一下Go泛型的现状,在介绍的过程中通过不断的思考,最终得出一个理由。什么是泛型泛型编程是一种编程语言的风格或范例。泛型允许程序员在用强类型语言编写代码时使用一些后来确定的类型,而它在实际实例化时不会为这些参数指定类型。另外,每种语言、其编译器、运行环境对泛型的支持不同,需要辩证。简单来说,泛型就是参数多态。可以根据实际参数类型生成不同的版本,支持任意多次调用:funcF(a,bT)T{returna+b}//TisintF(1,2)//TisstringF("1"","2")编译器在编译时确定T的输入参数类型。这也是Go的泛型实现“编译时类型安全”的要求之一。为什么需要泛型这时候可能有人会说不用泛型也行...我感觉写业务代码没什么作用,比起泛型还是把错误做好(针对具体的newnews,请参考:Heavy:Goerrorswillnotanyplansforfurtherimprovement)。但是,泛型有其自身所需的场景。最常见的就是基础库在处理和获取配置中心数据的时候,必须要对类型进行处理。经常会遇到下面的场景:手写一个“泛型”如果用接口(interface)按类型来做,就得切换(type)来枚举所有的基本类型。这显然是不合理的,也不可能做太复杂的逻辑,支持的类型还是泄露了。另外,单从语言层面来说,支持泛型是必然事件,因为泛型的存在对于解决特定领域的问题具有一定的意义。接口和泛型有什么区别?上面我们提到了接口类型。这时候泛型的第二个经典问题出现了。那就是“接口和泛型有什么区别?”为什么不用一个接口来实现“泛型”:typeTinterface{...}funcF(a,bT)T{returna+b}是这样的,但是在这里面有个致命的缺陷。即接口的输入输出参数在运行时可以表示为不同的类型:F("Friedfish",233)一定要做好,必须在内部断言参数,否则就是字符串类型friedfish而怎么把它加到int类型的233上,难免会报错。另一方面,从“泛型”的实际使用来看,编译器会保证泛型函数的输入输出参数必须是同一类型,并且有强制检查://Error:typecheckingfailedformmainF("Friedfish",233)//必须是同一类型才能正常运行F(666,233)两者有本质区别。泛型会更安全,可以确保在编译的早期发现错误,而不是等到运行时(并且可能存在隐藏的错误)。一般来说,泛型比接口有以下优点:更安全:可以在编译的早期发现错误。良好的性能:静态类型。过去:为什么这么长时间没有仿制药?在社区微信群里,看到有朋友在??抱怨“Go语言没有泛型?”。变相的看,可能会想“Go已经11岁了,2020年就没有泛型了?”。这显然是错误的,因为泛型在本质上并不是绝对必要的,更不是Go语言的早期目标,所以在过去的发展阶段,他们并没有过多地关注这一点,而是将重点放在了其他特性上。另外,Go语言过去其实也进行过大量的泛型proposal实验。基本时间线(via@changkun)如下:虽然偶尔有中断,但仔细一看,我在2010年试过,现在2020年了。也很励志,显然官方也在进行中寻找方法和尝试,但一直没有找到更好的解决方案,大部分都存在问题,社区对解决方案的争论不断。现在:GoGenerics有两种方法可以尝试泛型。在线IanLanceTaylor提供了一个在线编译的go2go[2]:另一个是离线的,即在本地安装Go的特定分支版本:$gitclonehttps://github.com/golang/go$gitcheckoutdev.go2go$cdsrc&&。/all.bash不过这种本地安装的方式会比较费时间。初次尝试,推荐使用go2go。在尝鲜中,可以看到代码块中声明了一个Print方法,其函数签名体分为三部分:乍一看函数签名,变量T的关键字any是什么?你可能听说过早期泛型说到契约,这是契约吗?其实严格来说并不是,因为为了进一步简化语法,2020.06.07官方已经移除了contract。它已经被改造,现在只需要编写参数化接口。上面的any关键字是一个预定义的类型约束,它允许在声明后将任何类型用作类型参数,并允许函数用于任何类型的操作。从语法分析来看,Print方法包含以下属性(从左到右):类型列表:将输入参数的类型列表声明为一个T变量,可以传递任何类型的参数。形参列表:声明入参的形参列表是T变量的一个切片,形参为s。返回类型列表:声明函数的返回参数列表。上面的函数签名是Go泛型的基本外观。由于本文不是CRUD泛型,案例就不展开了(现有的泛型还不成熟)。如果你有兴趣,我强烈推荐阅读提案:TypeParameters-DraftDesign[3]。为什么不在通用战争中使用尖括号?社区很多同学都在讨论的一个问题是“为什么Go泛型不像C++和Java那样使用尖括号?”还出现了“Go一直是业界工程实践的典范,你为什么不直接用尖括号”?我们思考问题的时候不要只看表面,官方说没有,那么我们可以回过头来看,Go语言使用尖括号:funcprint
