除了别人说的(特别是PeterSO
and dskinner
-在他对彼得的回答的评论中),请注意几件重要的事情:
您可以像调用简单函数一样调用方法
在Go中,你可以调用any方法函数不是作为接收器上的方法,而是作为常规函数 - 只需用它定义为方法的类型的名称来限定其名称即可明确地向它传递一个接收者参数(从方法中获取一个简单的函数称为
用一个方法表达式 https://golang.org/ref/spec#Method_expressions).
展示:
package main
import "fmt"
type Foo int
func (f Foo) Bar() {
fmt.Printf("My receiver is %v\n", f)
}
func main() {
a := Foo(46)
a.Bar()
b := Foo(51)
Foo.Bar(b)
}
(游乐场链接。 http://play.golang.org/p/hGDELp3OBA)
运行时,该程序打印:
My receiver is 46
My receiver is 51
如你看到的,self
在这里失去了它的神圣意义,因为你刚刚调用了一个方法人为地为其构建上下文,这与经常引用的“调用对象的方法是将消息传递给该对象”的概念无关。
回顾一下,在 Go 中,方法只是一个在语义上绑定到特定类型的函数,它接收一个额外的参数(它的接收者),无论它是如何调用的。与许多其他主流语言相反,Go 并没有隐藏这一事实。
接收器不一定是mutable在其类型定义的方法内
正如我的示例所示,我定义了一个方法,Bar()
,在非指针接收器上,如果您尝试向接收器分配一个值,该值将成功但不会影响调用者,因为接收器(与 Go 中的所有内容一样)已按值传递(因此整数已刚刚被复制)。
为了能够改变方法中接收者的值,您必须在适当类型的指针上定义它,例如
func (f *Foo) Bar() {
// here you can mutate the value via *f, like
*f = 73
}
再次,您可以看到使用self
意思是“我”,“我的内部”在这里变得毫无意义:在我的示例中,该方法仅接收一个它知道的类型的值。您可以看到这与许多面向对象语言形成鲜明对比,在许多面向对象语言中,对象是通常通过引用传递的黑匣子。在 Go 中,您几乎可以在任何东西上定义方法(包括其他方法 http://play.golang.org/p/eeT3WXFHH3,这是由net/http
顺便说一下,标准包)这削弱了“方法是针对对象的”概念。
不同的方法集可能在不同的时间适用于相同的值
在 Go 中,方法是围绕特定类型对功能进行分组的便捷方法,并且不同的方法集可能适用于程序流的不同点中的相同值。结合他们提供的接口和鸭子类型,这个概念确实蓬勃发展。这个想法是,在 Go 中,有一种定义“支持”类型的习惯用法,这些类型对其他类型的值执行某些操作。
一个很好的例子是标准套餐sort http://golang.org/pkg/sort/:例如,它提供了类型IntSlice
它允许您对整数切片(类型的值)进行排序[]int
。为此,您可以将切片类型转换为sort.IntSlice
结果得到的值有一整套用于对切片进行排序的方法,同时你的价值的内在表现并没有改变- 因为sort.IntSlice
is 定义为type IntSlice []int http://golang.org/pkg/sort/#IntSlice。在每个方法中IntSlice
类型,很难将它们的接收者值的含义与self
——仅仅因为该类型的存在只是为了为另一种类型提供一组方法;从哲学意义上来说,这种实用类型没有“自我”的概念;-)
结论
所以我想说,让事情在你的头脑中保持简单,不要试图用它不具备的语义“超载”Go 所采用的清晰简单的方法。明确地说明它提供的。
还有一点注意。我个人对 Go 习语的看法是,Go 最重要的特性是它的实用性(而不是理想主义等),所以如果你看到一些“感觉”不自然的概念,请尝试解决why它就是这样设计的,大多数时候你会发现为什么这个概念会在你的大脑中“点击”并变得自然。 (我必须承认,要理解 Go 中的方法来解决这个特殊问题,需要对C
将会有很大帮助。)