Go的方法Method
在面向對象的語言中,類可以包括屬性和方法;而在Go中,並無類的概念,往往使用結構體struct
來替代類的操作,但結構體中只有字段屬性,那方法在哪裏?
Go爲我們提供了一種名爲Method
的特殊函數,它通過作用在某個接收者上面來實現與其關聯,可以實現類的方法這一需求。
- Go的方法是一種特殊的函數,與普通函數的區別在於,方法需要一個接收者(receiver),它是作用在接收者上的函數,接收者是某種類型的變量
- 接收者幾乎可以是任何的類型,不僅僅是結構體struct,還可以是整型、浮點型、布爾型、數組等等,但是不能是接口。因爲接口是一個抽象定義,但是方法卻是具體實現;使用接口作接收者會引發一個編譯錯誤:
invalid receiver type
- 使用結構體加上它的方法其實就類似實現了面向語言中的類,但不同的是,Go拓展了“類”的範圍,Go的方法可以在任何類型上面添加,不侷限於結構體
- 在Go中,類型的代碼和綁定在它上面的代碼可以分開存在於不同的源文件中,不一定要放置在一起。但至少,它們必須是同一個包的(如果方法和接收者類型在不同包,方法會找不到他的接收者)。在同一個包中,方法可以訪問到類型中的屬性字段(無論是否大寫)
- 方法是一種函數,因此不支持重載,即對於某個類型,不能有多個相同名字的方法。但是,如果接收者類型不同,那麼允許相同名字的方法存在,即相同名字的方法可以在多個不同的類型接收者上存在。如下
func (a int) add(b int) int {return a + b} func (a float32) add(b float32) float32 {return a+b}
方法的定義及使用
Go方法定義的格式
func (recv receiver_type) methodName(parameter_list) (return_value_list) {
...
}
下面是一個方法使用的實例,我們在結構體student
上面添加上方法Study
。在方法定義的第一個括號中指定接收者變量
type student struct {
name string
id int
}
func main(){
s := student{
name : "xiao",
id : 1,
}
s.Study()
}
func (a student) Study(){
fmt.Println(a.name, "is studying")
}
上面是對結構體添加方法,我們還可以結合類型別名對其它任意類型來添加方法
type zhengxing int
func main(){
var a zhengxing
a.Print()
}
func (i zhengxing) Print(){
fmt.Println("zhengxing")
}
在上面的代碼中,我們在主程序中調用方法都是先創建某個類型的變量,然後通過變量來調用方法,這種方式稱之爲Method Value,還有另外一種調用方法的方法,稱之爲Method Expression,它是通過類型來調用方法,並將類型的變量作爲參數傳遞到方法中:
type zhengxing int
func main(){
var a zhengxing
zhengxing.Print(a)
}
func (i zhengxing) Print(){
fmt.Println("zhengxing")
}
方法接收者的值傳遞與指針傳遞
方法中對接收者默認也是值傳遞,即傳入方法的接收者爲原始接收者的拷貝,在方法中無法直接改變真正的接收者變量。如果確實需要在方法中改變接收者,可以使用接收者的指針來傳遞:
type student struct {
name string
id int
}
func main(){
s := student{
name : "xiao",
id : 1,
}
s.changeId(110)
fmt.Println(s.id)
}
func (a *student) changeId(id int){
a.id = id
fmt.Println(a.id)
}
從性能方面考慮,傳遞一個地址比起拷貝一個類型變量更加具有優勢,因此更加推薦這種傳遞接收者類型的指針。而且我們也可以看到在訪問結構體字段時用法和非指針的結構體的用法是相同的,Go在內部幫我們自動作了轉換
內嵌方法的繼承
之前在上一篇文章中提到了結構體內可以通過內嵌結構體的方式來實現類似於繼承內嵌結構體字段的效果,現在我們增加了方法的使用,因此,如果內嵌結構體擁有一個方法,那麼外層的結構體同樣繼承了內嵌結構體的方法,可以直接通過外層結構體直接訪問到該方法。而如果外層結構體定義了一個同名的方法,那麼外層的方法將覆蓋掉內嵌方法,直接通過外層結構體訪問該方法將訪問到外層結構體關聯的方法,而如果要使用內嵌方法,則需要一層一層嵌套訪問。
是不是感覺很熟悉?沒錯,這個解決方式和字段名衝突時的解決方式是相同的。