Go開發工程師
未來3-5年企業高性能項目不可替代的語言,從基礎到項目實戰再到重構,真正從入門到精通
我們在前面多次提到過指針及指針類型。例如,*Person是Person的指針類型。又例如,表達式&p的求值結果是p的指針。方法的接收者類型的不同會給方法的功能帶來什么影響?該方法所屬的類型又會因此發生哪些潛移默化的改變?現在,我們就來解答第一個問題。至于第二個問題,我會在下一小節予以解答。
指針操作涉及到兩個操作符——&和*。這兩個操作符均有多個用途。但是當它們作為地址操作符出現時,前者的作用是取址,而后者的作用是取值。更通俗地講,當地址操作符&被應用到一個值上時會取出指向該值的指針值,而當地址操作符*被應用到一個指針值上時會取出該指針指向的那個值。它們可以被視為相反的操作。
除此之外,當*出現在一個類型之前(如*Person和*[3]string)時就不能被看做是操作符了,而應該被視為一個符號。如此組合而成的標識符所表達的含義是作為第二部分的那個類型的指針類型。我們也可以把其中的第二部分所代表的類型稱為基底類型。例如,*[3]string是數組類型[3]string的指針類型,而[3]string是*[3]string的基底類型。
好了,我們現在回過頭去再看結構體類型Person。它及其兩個方法的完整聲明如下:
type Person struct {
Name string
Gender string
Age uint8
Address string
}
func (person *Person) Grow() {
person.Age++
}
func (person *Person) Move(newAddress string) string {
old := person.Address
person.Address = newAddress
return old
}
注意,Person的兩個方法Grow和Move的接收者類型都是*Person,而不是Person。只要一個方法的接收者類型是其所屬類型的指針類型而不是該類型本身,那么我就可以稱該方法為一個指針方法。上面的Grow方法和Move方法都是Person類型的指針方法。
相對的,如果一個方法的接收者類型就是其所屬的類型本身,那么我們就可以把它叫做值方法。我們只要微調一下Grow方法的接收者類型就可以把它從指針方法變為值方法:
func (person Person) Grow() {
person.Age++
}
那指針方法和值方法到底有什么區別呢?我們在保留上述修改的前提下編寫如下代碼:
p := Person{"Robert", "Male", 33, "Beijing"}
p.Grow()
fmt.Printf("%v\n", p)
這段代碼被執行后,標準輸出會打印出什么內容呢?直覺上,34會被打印出來,但是被打印出來的卻是33。這是怎么回事呢?Grow方法的功能失效了?!
解答這個問題需要引出一條定論:方法的接收者標識符所代表的是該方法當前所屬的那個值的一個副本,而不是該值本身。例如,在上述代碼中,Person類型的Grow方法的接收者標識符person代表的是p的值的一個拷貝,而不是p的值。我們在調用Grow方法的時候,Go語言會將p的值復制一份并將其作為此次調用的當前值。正因為如此,Grow方法中的person.Age++語句的執行會使這個副本的Age字段的值變為34,而p的Age字段的值卻依然是33。這就是問題所在。
只要我們把Grow變回指針方法就可以解決這個問題。原因是,這時的person代表的是p的值的指針的副本。指針的副本仍會指向p的值。另外,之所以選擇表達式person.Age成立,是因為如果Go語言發現person是指針并且指向的那個值有Age字段,那么就會把該表達式視為(*person).Age。其實,這時的person.Age正是(*person).Age的速記法。
在命令源碼文件index.go中,我已經編寫好了結構體類型MyInt及其兩個方法。我希望在該文件被運行之后在標準輸出上打印出true。但是好像哪里出了問題,致使標準輸出上總是出現false。你能幫我找出問題所在并修正它嗎?要求僅對一處做出修改。
把MyInt類型的Decrease方法由值方法改為指針方法即可。至于怎樣改請回顧本節的“知識要點”。
請驗證,完成請求
由于請求次數過多,請先驗證,完成再次請求
打開微信掃碼自動綁定
綁定后可得到
使用 Ctrl+D 可將課程添加到書簽
舉報