天天看點

Go學習筆記(12)Go方法MethodGo的方法Method方法的定義及使用方法接收者的值傳遞與指針傳遞内嵌方法的繼承

文章目錄

  • Go的方法Method
  • 方法的定義及使用
  • 方法接收者的值傳遞與指針傳遞
  • 内嵌方法的繼承

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在内部幫我們自動作了轉換

内嵌方法的繼承

    之前在上一篇文章中提到了結構體内可以通過内嵌結構體的方式來實作類似于繼承内嵌結構體字段的效果,現在我們增加了方法的使用,是以,如果内嵌結構體擁有一個方法,那麼外層的結構體同樣繼承了内嵌結構體的方法,可以直接通過外層結構體直接通路到該方法。而如果外層結構體定義了一個同名的方法,那麼外層的方法将覆寫掉内嵌方法,直接通過外層結構體通路該方法将通路到外層結構體關聯的方法,而如果要使用内嵌方法,則需要一層一層嵌套通路。

    是不是感覺很熟悉?沒錯,這個解決方式和字段名沖突時的解決方式是相同的。

繼續閱讀