
在Go中,函数类型是一等类型,这意味着可以把函数当作一个值来传递和使用。函
数值既可以作为其他函数的参数,也可以作为其结果。另外,我们还可以利用函数类型
的这一特性生成闭包。
一个函数的声明通常包括关键字func、函数名、分别由圆括号包裹的参数列表和结
果列表,以及由花括号包裹的函数体,就像这样:
func divide(dividend int,divisor int)(int,error){//省略部分代码}
函数可以没有参数列表,也可以没有结果列表,但空参数列表必须保留括号,而空
结果列表则不用,示例如下:
func printTab(){//省略部分代码
另外,参数列表中的参数必须有名称,而结果列表中结果的名称则可有可无。不过
结果列表中的结果要么都省略名称,要么都要有名称。带有结果名称的divide函数的声
明如下:
func divide(dividend int,divisor int)(result int,err error){//省略部分代码}
如果函数的结果有名称,那么在函数被调用时,以它们为名的变量就会被隐式声明。
如此一来在函数中就可以直接使用它们了,就像使用参数那样。给代表结果的变量赋值,
就相当于设置函数的返回结果。

函数体中每个条件分支的最后一般都要有return语句,该语句以return关键字开始,
后跟与函数结果列表相匹配的变量、常量、表达式或值。无论是什么,它们都会被求值
并得到确切的值。但是,如果函数声明的结果是有名称的,那么return关键字后面就不
用追加任何东西了。divide函数的完整声明可以这样:
func divide(dividend int,divisor int)(result int,err error){if divisor==0{err=errors.New("division by zero")returnresult=dividend/divisorreturn
其中,errors是一个标准库代码包的名称,而其中的New函数专用于生成error类型的值。
Go编程有一个惯用法,即把error类型的结果作为函数结果列表的最后一员。
当然,你可以编写自己的divide函数,不过在这之前需要把它提升成一个类型:
//用于定义二元操作的面数类型
type binaryoperation func(operandt int,operand2int)(result int,err error)
除法是一个二元操作,所以我做了进一步的范化。再编写一个函数,你就会知道这
样做的意义了:
//用于以自定义的方式执行二无操作func operate(op1 int,op2 int,bop binaryoperation)(result int,err error){if bop==nil{err=errors.New("invalid binary operation function")returnreturn bop(op1,op2)
这里实际上实现了一个闭包,我把二元操作的实现权留给了operate函数的使用者。
作为一等类型的函数类型让程序的灵活性大大增加,接口不再是定义行为的唯一途径了。
顺便说一句,函数类型的零值是ni1。检查外来函数值是否非ni1总是有必要的。
方法是函数的一种,它实际上就是与某个数据类型关联在一起的函数,示例如下:
type myInt intfunc(i myInt)add(another int)myInt{i=i+myInt(another)return i
从声明上看,方法只是在关键字func和函数名称之间,加了一个由圆括号包裹的接
收者声明。接收者声明由两部分组成:右边表明这个方法与哪个类型关联,这里是myInt;
左边指定这个类型的值在当前方法中的标识符,这里是i。这个标识符在当前方法中可以
看作一个变量的代表,就像参数那样。所以,它也可以称为接收者变量。不过这里有一
个问题,请看:
i1:=myInt(1)i2:=il.add(2)fmt.Print1n(i1,i2)
这3行代码执行后,会打印出13。i的值未改变,是因为在值方法中对接收者变量的赋
值一般不会影响到源值。这里,变量i1的值就是源值。在调用i1的add方法时,这个值
被赋给了接收者变量i(前者的副本与后者产生关联)。但是,i和i1是两个变量,它们
之间并不存在关联。

值方法的接收者类型是非指针的数据类型。相对应的是指针方法。它的接收者类型
是某个数据类型的指针类型。若把add方法改造成指针方法,则可以是:
func(i*myInt)add(another int)myInt{*i=*i+myInt(another)return*i
这时add方法是myInt的指针方法。这里请注意操作符*的用法。*myInt表示了myInt的
指针类型,而*i则表示指针i指向的值。经改造,前面那3行代码的执行效果就会是打
值方法和指针方法遵循了如下规则。
接收者变量代表的值实际上是源值的一个复制品。如果这个值不是指针类型的,
那么在值方法中自然就没有途径去改变源值。而指针值与其复制品指向的肯定是
同一个值,所以在指针方法中就存在了改变源值的途径。这里有一个例外,那就
是如果接收者类型是某个引用类型或它的别名类型,那么即使是值方法,也可以
改变源值。

对于某个非指针的数据类型,与它关联的方法的集合中只包含它的值方法。而对
于它的指针类型,其方法集合中既包含值方法也包含指针方法。不过,在非指针
数据类型的值上,也是能够调用其指针方法的。这是因为Go在内部做了自动转换。
例如,若add方法是指针方法,那么表达式i1.add(2)会被自动转换为(8i1).add(2)。
请注意,第二条规则对于编写接口类型的实现类型来说非常有用。