Go 语言之父早期提到过 less is more[1] 的哲学,可惜社区里有不少人被带偏了。

每次 Go 增加语法上的新特性、新功能,就会有原教旨主义者跳出来扔出一句 less is more(或者也可能是大道至简),扬长而去,留下风中凌乱的你。

即使到了官方已经确定要增加泛型功能的 2020 年,依然有人煞有介事地写文章说为什么 go doesn't need generics[2],作为理智的 Gopher,最好不要对别人的结论尽信,至少要看看其它语言社区怎么看待这件事情。

Java 社区是怎么理解泛型的必要性的呢?

简而言之,泛型使类型(类和接口)能够在定义类、接口和方法时成为参数。就像我们更熟悉的在方法声明中使用的形式参数一样,类型参数为你提供了一种用不同的输入重复使用相同代码的方法。不同的是,形式参数的输入是值,而类型参数的输入是类型。

与非泛型代码相比,使用泛型的代码有很多好处。

  • 在编译时进行强类型检查。Java 编译器对泛型代码进行强类型检查,如果代码违反类型安全就会报错。编译时的错误比运行时的错误更易修复。

  • 消除类型转换。下面这段代码片段在没有泛型时,需要类型转换:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

用泛型重写,代码不再需要进行类型转换:

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast
  • 程序员可以编写泛型算法 使用泛型可以实现在不同类型上都可以工作的泛型算法的同时,保证类型安全性。

Gopher 可能对“类型安全”不太熟悉,举个例子,我们可以用 gods 库来实现下面的数据结构。

package mainimport ("fmt"sll "github.com/emirpasic/gods/lists/singlylinkedlist"
)func main() {list := sll.New()list.Add("a")                     // ["a"]
}

我们的本意是实现一个 string 的单链表,但是通用的数据结构库没有办法阻止用户向该 list 内插入非 string 类型的值,比如用户可以这样:

 list := sll.New()list.Add("a")                     // ["a"]list.Add(2)                       // ["a", 2]

这显然不是我们想要的结果。

可见泛型最常见的场景是在类型安全的前提下实现算法流程,对于 Go 来说,我们使用的数据结构和算法来源有两个地方:container 标准库、第三方数据结构库,如 gods[3] 。

和我们前面举的例子一样,标准库的通用 container 的大多接口也是接收空 interface{},或返回空 interface{}:

package mainimport ("container/list"
)func main() {l := list.New()l.PushBack(4)l.PushFront("bad value")
}

做不到类型安全的话,那么用户代码就可能在运行期间发生断言产生的 panic/error。除了容器的功能容易被破坏,类似下面的 bug 也挺容易出现的:

package mainimport "fmt"type mystring stringfunc main() {var a interface{} = "abc"var b interface{} = mystring("abc")fmt.Println(a == b)
}

社区的其它尝试

社区曾经有一些靠代码生成实现的泛型库,如genny[4],其本质是使用文本替换来实现多种类型的代码生成。

genny 使用也比较简单,比如 example 里的例子:

package queueimport "github.com/cheekybits/genny/generic"// NOTE: this is how easy it is to define a generic type
type Something generic.Type// SomethingQueue is a queue of Somethings.
type SomethingQueue struct {items []Something
}func NewSomethingQueue() *SomethingQueue {return &SomethingQueue{items: make([]Something, 0)}
}
func (q *SomethingQueue) Push(item Something) {q.items = append(q.items, item)
}
func (q *SomethingQueue) Pop() Something {item := q.items[0]q.items = q.items[1:]return item
}

cat source.go | genny gen "Something=string"

// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/gennypackage queue// StringQueue is a queue of Strings.
type StringQueue struct {items []string
}func NewStringQueue() *StringQueue {return &StringQueue{items: make([]string, 0)}
}
func (q *StringQueue) Push(item string) {q.items = append(q.items, item)
}
func (q *StringQueue) Pop() string {item := q.items[0]q.items = q.items[1:]return item
}

想实现多种类型的结构就在生成代码时传入多种类型就可以了。

这种做法和人们调侃 Go 泛型时使用的 gif[5] 本质上也没什么区别。

语言的原生支持能让我们省事,并且也能在实现上更加严谨。

在 《Rise and Fall of Software Recipes》一书中,有这么一个故事:

Among the recent projects failing because (or despite) of strong processes, Obamacare is a telling example. It involves 50 contractors, has cost fortunes, was delivered late and crippled with bugs. It was developed using a typical waterfall process, and if only because of that, the Agile community started howling, claiming that they would have made the project a success[Healthcare.gov failure].

And when an Agile project fails like Universal Credit in Great-Britain[UniversalCredit] [NAO2013], even when the full report states that the lack of detailed blueprint – typical of Agile methodologies – was one of the factors that caused the failure, common Agile wisdom says it is because it was not applied properly, or should I say, not Agile enough.

简而言之,就是敏捷大师们其实非常双标,他们给出的方法论也不一定靠谱,反正成功了就是大师方法得当,失败了就是我们执行不力没有学到精髓。正着说反着说都有道理。

再看看现在的 Go 社区,buzzwords 也很多,如果一个特性大师不想做,那就是 less is more。如果一个特性大师想做,那就是 orthogonal,非常客观。

对于不想迷信大师的 Gopher 来说,多听听批评意见没坏处:go is not good[6]。

[1]

less is more: https://en.wikipedia.org/wiki/Less_is_more

[2]

go doesn't need generics: https://dzone.com/articles/go-doesnt-need-generics

[3]

gods: https://github.com/emirpasic/gods

[4]

genny: https://github.com/cheekybits/genny

[5]

gif: https://twitter.com/yogthos/status/883058510275149826

[6]

go is not good: https://github.com/ksimka/go-is-not-good

Go 还是需要泛型的相关推荐

  1. 【C#】集合_哈希表_字典_泛型_文件

    数组能做到:存放同种类型数据,且数据个数确定 object类型的数组能满足:放各种类型的数据,确定放多少个,但是随意插入元素,数组做不到 集合能做到:存放各种数据类型,且不确定存放多少个,能做到随意插 ...

  2. 2021年大数据常用语言Scala(三十六):scala高级用法 泛型

    目录 泛型 定义一个泛型方法 定义一个泛型类 上下界 协变.逆变.非变 非变 协变 逆变 泛型 scala和Java一样,类和特质.方法都可以支持泛型.我们在学习集合的时候,一般都会涉及到泛型. sc ...

  3. 对比两个同类型的泛型集合并返回差异泛型集合 ——两个List类名的比较

    1: /// <summary> 2: /// 对比两个同类型的泛型集合并返回差异泛型集合 3: /// </summary> 4: /// <typeparam nam ...

  4. 利用委托和泛型实现树的常用操作

    在日常开发中,经常遇到对树的操作,我们可以利用泛型和委托对这些树进行操作,这样就不需要每有一个树就要实现相应的功能了. 源码在http://files.cnblogs.com/haiconc/Lang ...

  5. java 泛型 父子,Java泛型-mb601cf8a78cc07的博客-51CTO博客

    Java泛型 泛型类 即把不确定的数据元素类型用一个泛型占位符表示@Data public class Person { private T name; private T address; }Per ...

  6. java 泛型 .net_Java基础11:Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 本文参考https://blog.csdn.net/s10461/article/details/53941091 具体代码在我的GitHub中可以找 ...

  7. java 获取泛型的type,如何获取泛型的Type类型

    开发中很多时候都遇到或使用到泛型.例如在json转换成bean对象或其他对象,而对象中存在泛型,这时候需要用到TypeToken. Type:是java里的 java.lang.reflect.Typ ...

  8. Java中创建泛型数组

    Java中创建泛型数组 使用泛型时,我想很多人肯定尝试过如下的代码,去创建一个泛型数组 T[] array = new T[]; 当我们写出这样的代码时编译器会报Cannot create a gen ...

  9. 第八章 泛型程序设计

    1.带有[超类型限定 super]的通配符可以向泛型对象写入,带有[子类型限定 extends]的通配符可以从泛型对象读取,反之则不然. 转载于:https://www.cnblogs.com/bao ...

  10. C# 篇基础知识11——泛型和集合

    .NET提供了一级功能强大的集合类,实现了多种不同类型的集合,可以根据实际用途选择恰当的集合类型. 除了数组 Array 类定义在System 命名空间中外,其他的集合类都定义在System.Coll ...

最新文章

  1. 太赞了!NumPy 手写所有主流 ML 模型,由普林斯顿博士后 David Bourgin打造的史上最强机器学习基石项目!...
  2. UIButton状态探索和自定义
  3. ubutu14.04无法使用sudo,也无法切换到root用户去解决问题怎么办?
  4. 大数据测试之hadoop命令大全 2
  5. 【题目分析】1059 Prime Factors (25 分)
  6. 【effective c++读书笔记】【第8章】定制new和delete(2)
  7. JAVA反射-面试题
  8. python实现面试程序
  9. MySQL自定义函数(四十六)
  10. Mysql的Root密码忘记,查看或修改的解决方法(图文介绍)
  11. 语音情感识别研究进展综述
  12. Java 添加、验证PDF 数字签名
  13. 写一篇meta分析要多少时间?如何写好一篇Meta分析,你需要这样做
  14. Spring常用注解含义
  15. EntityRef:expecting“;”
  16. Java为PDF文档加密
  17. 冰达ROS机器人使用-实现slam建模、自主导航、避障
  18. python使用tkinter库,封装操作excel为GUI程序
  19. 怎么把视频转成mp3音频?
  20. linux内核c1bcbc40,Linux内核驱动

热门文章

  1. python实现一个商品管理_python编写商品管理
  2. liunxC下零碎知识点的总结
  3. 深度解析利用ES6进行Promise封装总结
  4. 解决JBoss只能通过localhost访问不能通过IP的问题
  5. 158.5. manifests
  6. MySQL从库的列类型不一致导致的复制异常问题
  7. spring-注入array集合
  8. Robotium todolist.test.testcases.logout
  9. jquery.validation.js 使用
  10. android 官方教程中文版