Go对C语言的很多语法特性做了改良,正如Rob Pike在《Less is Exponentially More》中提到,Go的“起点: C语言,解决一些明显的瑕疵、删除杂质、增加一些缺少的特性。”,比如,把switch/case的case子程序段默认break跳出,case语句支持数值范围、条件判断语句;所有类型默认初始化为0,没有未初始化变量;把类型放在变量后面的声明语法(链接),使复杂声明更加清晰易懂;没有头文件,文件的编译以包组织,改善封装能力;用空接口(interface {})代替void *的位置,提高类型系统能力等等。
Go对函数,方法,接口做了清晰的区分。与Erlang类似,Go的函数作为第一公民。函数可以让我们将一个语句序列打包为一个单元,然后可以从程序中其它地方多次调用。函数和方法的区别是指有没有接收器,而不像其他语言那样是指有没有返回值。接口类型具体描述了一系列方法的集合,而空接口interfac{}表示可以接收任意类型。接口的这2中使用方式,用面对对象编程范式来类比的话,可以类比于subtype polymorphism(子类型多态)和ad hoc polymorphism(非参数多态)。
从图中示例可以看出,Go的goroutine就是一个函数,以及在堆上为其分配的一个堆栈。所以它非常廉价,我们可以很轻松的创建上万个goroutine,但它们并不是被操作系统所调度执行。goroutine只能使用channel来发送给指定的goroutine请求来查询更新变量。这也就是Go的口头禅“不要使用共享数据来通信,使用通信来共享数据”。channel支持容量限制和range迭代器。
Go/Python/Erlang语言词法对比
Go、Python、Erlang三种语言词法符号的详细对比如下(点击见完整大图)。Go的词法符号是3个语言中最多的,有41个,而且符号复用的情况也较多。相对来说,Python最少,只有31个。
Go语言在词法和代码格式上采取了很强硬的态度。Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。这种限制包内成员的方式同样适用于struct或者一个类型的方法。
除了关键字,此外,Go还有大约30多个预定义的名字,比如int和true等,主要对应内建的常量、类型和函数。
以_test.go为后缀名的源文件是测试文件,它们是go test测试的一部分。测试文件中以Test为函数名前缀的函数是测试函数,用于测试程序的一些逻辑行为是否正确;以Benchmark为函数名前缀的函数是基准测试函数,它们用于衡量一些函数的性能。
TDD Go编程示例
本小节以TDD方式开发一个斐波那契算法的方式,展示Go的特性、语法和使用方式,如Go的单元测试技术,并发编程、匿名函数、闭包等。
首先单元测试文件如下:
package main import ( "testing" ) func TestFib(t *testing.T) { var testdatas = []struct { n int want int64 }{ {0, 0}, {1, 1}, {2, 1}, {3, 2}, {4, 3}, {16, 987}, {32, 2178309}, {45, 1134903170}, } for _, test := range testdatas { n := test.n want := test.want got := fib(n) if got != want { t.Errorf("fib(%d)=%d, want %d\n", n, got, want) } } }