什么是方法
一個方法只是一個函數,它有一個特殊的接收者(receiver)類型,該接收者放在 func
關鍵字和函數名之間。接收者可以是結構體類型或非結構體類型。可以在方法內部訪問接收者。
通過下面的語法創建一個方法:
func (t Type) methodName(parameter list) {
}
上面的代碼片段創建了一個名為 methodName
的方法,該方法有一個類型為 Type
的接收者。
例子
讓我們編寫一個簡單的程序,它創建一個結構體類型的方法並調用它。
package main import ( "fmt" ) type Employee struct { name string salary int currency string } /* displaySalary() method has Employee as the receiver type */ func (e Employee) displaySalary() { fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary) } func main() { emp1 := Employee { name: "Sam Adolf", salary: 5000, currency: "$", } emp1.displaySalary() //Calling displaySalary() method of Employee type }
上面程序的第 6 行,我們創建了 Employee
的一個名為 displaySalary
的方法。在 displaySalary()
方法內部可以訪問它的接收者 e
(類型為 Employee
)。在第 17 行,我們使用接收者 e
,並打印它的 name
,currency
以及 salary
。
在第26行,我們使用 emp1.displaySalary()
這樣的語法來調用方法。
程序的輸出為:Salary of Sam Adolf is $5000
。
為什么使用方法而不是函數?
上面的程序可以使用函數而不是方法重寫如下
package main import ( "fmt" ) type Employee struct { name string salary int currency string } /* displaySalary() method converted to function with Employee as parameter */ func displaySalary(e Employee) { fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary) } func main() { emp1 := Employee{ name: "Sam Adolf", salary: 5000, currency: "$", } displaySalary(emp1) }
在上面的程序中,我們使用 displaySalary
函數替換了方法,並將 Employee
結構體作為參數傳給它。該程序的輸出與上面的程序輸出一樣:Salary of Sam Adolf is $5000
。
為什么使用方法而不是函數?
上面的程序可以使用函數而不是方法重寫如下:
package main import ( "fmt" ) type Employee struct { name string salary int currency string } /* displaySalary() method converted to function with Employee as parameter */ func displaySalary(e Employee) { fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary) } func main() { emp1 := Employee{ name: "Sam Adolf", salary: 5000, currency: "$", } displaySalary(emp1) }
在上面的程序中,我們使用 displaySalary
函數替換了方法,並將 Employee
結構體作為參數傳給它。該程序的輸出與上面的程序輸出一樣:Salary of Sam Adolf is $5000
。
那么為什么我們可以用函數完成同樣的工作,卻還要使用方法呢?這里有幾個原因,我們一個一個地看。
- Go 不是一個純面向對象的編程語言,它不支持 class 類型。因此通過在一個類型上建立方法來實現與 class 相似的行為。
- 同名方法可以定義在不同的類型上,但是 Go 不允許同名函數。假設我們有一個
Square
和Circle
兩個結構體。在Square
和Circle
上定義同名的方法是合法的,比如下面的程序:
package main import ( "fmt" "math" ) type Rectangle struct { length int width int } type Circle struct { radius float64 } func (r Rectangle) Area() int { return r.length * r.width } func (c Circle) Area() float64 { return math.Pi * c.radius * c.radius } func main() { r := Rectangle{ length: 10, width: 5, } fmt.Printf("Area of rectangle %d\n", r.Area()) c := Circle{ radius: 12, } fmt.Printf("Area of circle %f", c.Area()) }
程序的輸出為:
Area of rectangle 50 Area of circle 452.389342
定義非結構體類型的方法
現在我們定義的都是結構體類型的方法。同樣可以定義非結構體類型的方法,不過這里需要注意一點。為了定義某個類型的方法,接收者類型的定義與方法的定義必須在同一個包中。目前為止,我們定義的結構體和相應的方法都是在main包中的,因此沒有任何問題。
package main func (a int) add(b int) { } func main() { }
在上面的程序中,第3行我們試圖添加一個方法 add
給內置類型 int
。這是不允許的,因為定義方法 add
所在的包和定義類型 int
的包不是同一個包。這個程序將會報編譯錯誤:cannot define new methods on non-local type int
。
使其工作的方法為定義內置類型的別名,然后以這個新類型為接收者創建方法。
package main import "fmt" type myInt int func (a myInt) add(b myInt) myInt { return a + b } func main() { num1 := myInt(5) num2 := myInt(10) sum := num1.add(num2) fmt.Println("Sum is", sum) }
上面的程序中,第5行,我們創建了新的類型,一個 int
的別名 myInt
。在第7行,我們定義了一個方法 add
,以 myInt
作為接收者。
程序的輸出為: Sum is 15
。