方法主要源於 OOP 語言,在傳統面嚮對象語言中 (例如 C++), 我們會用一個“類”來封裝屬於自己的數據和函數,這些類的函數就叫做方法。
雖然 Go 不是經典意義上的面嚮對象語言,但是我們可以在一些接收者(自定義類型,結構體)上定義函數,同理這些接收者的函數在 Go 裏面也叫做方法。
聲明
方法(method)的聲明和函數很相似, 只不過它必須指定接收者,我們先來看個簡單例子:
package main
type T struct{}
func (t T) F() {}
func main() {
t := T{}
t.F()
}
接收者類型不是任意類型
例如:
package main
func (t int64) F() {}
func main() {
t := int64(10)
t.F()
}
當運行以下代碼會得到 cannot define new methods on non-local type int64 類似錯誤信息,我們可以使用自定義類型來解決:
package main
type T int64
func (t T) F() {}
func main() {
t := T(10)
t.F()
}
小結:接收者不是任意類型,它只能爲用關鍵字 type 定義的類型(例如自定義類型,結構體)。
命名衝突
a. 接收者定義的方法名不能重複, 例如:
package main
type T struct{}
func (T) F() {}
func (T) F(a string) {}
func main() {
t := T{}
t.F()
}
運行代碼我們會得到 method redeclared: T.F 類似錯誤。
b. 結構體方法名不能和字段重複,例如:
package main
type T struct{
F string
}
func (T) F(){}
func main() {
t := T{}
t.F()
}
運行代碼我們會得到 : type T has both field and method named F 類似錯誤。
小結: 同一個接收者的方法名不能重複 (沒有重載);如果是結構體,方法名不能和字段重複。
接收者可以同時爲值和指針
在 Go 語言中,方法的接收者可以同時爲值或者指針,例如:
package main
type T struct{}
func (T) F() {}
func (*T) N() {}
func main() {
t := T{}
t.F()
t.N()
t1 := &T{} // 指針類型
t1.F()
t1.N()
}
可以看到無論值類型 T 還是指針類型 &T 都可以同時訪問 F 和 N 方法。
值和指針作爲接收者的區別
同樣我們先看一段代碼:
package main
import "fmt"
type T struct {
value int
}
func (m T) StayTheSame() {
m.value = 3
}
func (m *T) Update() {
m.value = 3
}
func main() {
m := T{0}
fmt.Println(m) // {0}
m.StayTheSame()
fmt.Println(m) // {0}
m.Update()
fmt.Println(m) // {3}
}
運行代碼輸出結果爲:
{0}
{0}
{3}
小結:值作爲接收者(T) 不會修改結構體值,而指針 *T 可以修改。
總結
可以看到在 Go 語言中方法有一些比較特殊的地方,主要爲以下幾點:
接收者的類型只能爲用關鍵字 type 定義的類型,例如自定義類型,結構體。
同一個接收者的方法名不能重複 (沒有重載),如果是結構體,方法名還不能和字段名重複。
值作爲接收者無法修改其值,如果有更改需求,需要使用指針類型。