原問(wèn)題
我回憶了一下,沒(méi)有任何博客或者視頻的作者在Go中以self或this作為方法的receiver,但是在stack overflow的很多問(wèn)題下面就經(jīng)常能看見(jiàn)有人以self或this作為方法的receiver。這讓我感到疑惑,用self作為receiver名有什么問(wèn)題嗎?
好像曾經(jīng)在哪里看到過(guò)有人說(shuō)receiver并不完全是一個(gè)指向自身的指針,誰(shuí)能解釋一下這種說(shuō)法嗎。把self看做指向自身的指針有什么問(wèn)題嗎?
例子:
type MyStruct struct {
Name string
}
那種方式比較合適呢,還是兩種都o(jì)k?
func (m *MyStruct) MyMethod() error {
// do something useful
}
or
func (self *MyStruct) MyMethod() error {
// do something useful
}
回答
其他的人都說(shuō)的很好了,我再補(bǔ)充幾點(diǎn)重要的:
以普通函數(shù)的方式調(diào)用Method Set里的方法
在Go里,不但可以通過(guò)receiver的方式調(diào)用一個(gè)方法,還可以像一個(gè)普通的函數(shù)一樣調(diào)用。在type的名稱后跟上相應(yīng)方法的名稱并將receiver作為第一個(gè)參數(shù)傳入(這種使用方式叫做使用method expression)。
demo:
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)
}
運(yùn)行結(jié)果:
My receiver is 46
My receiver is 51
可以看出來(lái),你手動(dòng)調(diào)用并賦予了執(zhí)行的上下文環(huán)境,使得self失去了他的語(yǔ)義,與面向?qū)ο缶幊讨袕V泛引用的一句話“調(diào)用一個(gè)對(duì)象的方法就是給這個(gè)對(duì)象傳遞信息”的概念沒(méi)有一點(diǎn)聯(lián)系。
總的來(lái)說(shuō),在Go里方法其實(shí)就是將函數(shù)語(yǔ)義化地綁定在指定type上,并接受一個(gè)叫做receiver的參數(shù),不管你以哪種方式調(diào)用方法,調(diào)用過(guò)程其實(shí)都是一樣的。與大多數(shù)主流語(yǔ)言不同,Go并沒(méi)有將方法調(diào)用的細(xì)節(jié)隱藏起來(lái)。
receiver并不一定能在方法內(nèi)部被改變
在上面的demo中可以看到我定義了一個(gè)Bar()方法,他的receiver并不是一個(gè)指針類型,如果試著在方法內(nèi)部為receiver賦值就會(huì)發(fā)現(xiàn)賦值是可以賦值,但是并不會(huì)影響到方法的調(diào)用者,因?yàn)閞eceiver的值(Go中所有的值都一樣)是按值傳遞的(所以這里的receiver只是調(diào)用者的一份拷貝)。
為了能在方法里改變r(jià)eceiver能影響到方法調(diào)用者,你需要定義一個(gè)指針類型的receiver:
func (f *Foo) Bar() {
// here you can mutate the value via *f, like
*f = 73
}
例子中的方法接收了一個(gè)確定類型的值,可以看出使用self表示“自身”已經(jīng)毫無(wú)意義了。這與傳統(tǒng)面向?qū)ο笳Z(yǔ)言默認(rèn)將對(duì)象按引用傳遞不同,在Go里你可以在任何東西“里”定義方法(包括在方法上定義方法,net/http標(biāo)準(zhǔn)庫(kù)就是這么做的),“對(duì)象里的方法”這種概念已經(jīng)不復(fù)存在了。
同樣的值在不同時(shí)候可以有不同的方法
在Go里,方法可以非常方便用與組織特定類型的各種功能。在代碼執(zhí)行期間,同樣的值可以擁有有不一樣的方法,結(jié)合Go提供的接口,鴨子類型的編碼風(fēng)格,使得這種(動(dòng)態(tài)織入方法)編碼方式非常流行。我們經(jīng)常能看到編碼時(shí)會(huì)定義一個(gè)“Support”類型,在其中放很多為其他不同類型提供的方法。
標(biāo)準(zhǔn)庫(kù)sort就是一個(gè)很好的例子:在sort包里提供一種IntSlice類型,這種類型允許你對(duì)整數(shù)類型的slice([]int)進(jìn)行排序。只要將slice轉(zhuǎn)換為IntSlice之后就擁有了各種對(duì)slice進(jìn)行排序的方法,并且排序時(shí)原來(lái)的slice值并不會(huì)被改變(因?yàn)?code>IntSlice就是type IntSlice []int)。很難去說(shuō)IntSlice里的所有方法的receive帶有self這層含義,因?yàn)檫@里所有的方法都是提供給其他類型使用的。從哲♂學(xué)的角度上看,這些工具類型并不存在“self”的概念。
總結(jié)
所以,應(yīng)該讓思維保持簡(jiǎn)單,不要給自己的想法加上太多的枷鎖,并不一定要使用語(yǔ)義化的方式去解釋Go所提供的清晰明了的編碼哲學(xué)。我自己對(duì)Go語(yǔ)言的學(xué)習(xí)理解來(lái)看,Go語(yǔ)言首要的編碼風(fēng)格應(yīng)該是實(shí)用(與務(wù)虛相反)。所以每當(dāng)你“感覺(jué)”某些概念很不自然,你就可以試著去弄清楚為什么這些東西在Go里要這樣設(shè)計(jì),大多數(shù)情況你最后會(huì)發(fā)現(xiàn)這種設(shè)計(jì)真是精妙。(我必須承認(rèn)熟悉C語(yǔ)言對(duì)于理解Go語(yǔ)言里methods設(shè)計(jì)有很大的幫助,更有助于理解這個(gè)問(wèn)題。)