Go語(yǔ)言同時(shí)支持函數(shù)和方法,方法是包含接收器的函數(shù),接收器可以是命名類型或結(jié)構(gòu)體類型的值或指針。為特定類型定義的方法屬于該類型的方法集。
- 方法在接收者參數(shù)的幫助下,可訪問接收者的屬性。
- 方法的接收者可以是結(jié)構(gòu)體類型或非結(jié)構(gòu)體類型。
方法會(huì)在func關(guān)鍵字和方法名之間添加一個(gè)特殊的接收器類型,接收器可以是結(jié)構(gòu)體或非結(jié)構(gòu)體類型,接收器可以在方法內(nèi)部被訪問。
func (receiver_name T)method_name(parameter_list) (return_type){
}
類型T上所有方法的集合稱之為類型T的方法集
Go語(yǔ)言不是純粹的面向?qū)ο蟮木幊陶Z(yǔ)言,Go語(yǔ)言不支持類。因此,基于類型的方法是一種實(shí)現(xiàn)和類相似行為的途徑。
Go語(yǔ)言中結(jié)構(gòu)體像是類的一種簡(jiǎn)化形式,如果將結(jié)構(gòu)體視為類,則類的方法就是作用在接收器上的函數(shù)。接收本身是某種類型的變量,因此方法是一種特殊類型的函數(shù)。
Go語(yǔ)言支持方法,方法與函數(shù)相似,不同之處在于方法中會(huì)包含一個(gè)接收者參數(shù)。創(chuàng)建方法時(shí),接收者和接收者類型必須出現(xiàn)在同一個(gè)包中,禁止創(chuàng)建方法時(shí)的接收者在其它包中已經(jīng)被定義。
接收器類型可以是任何類型,但接收器不能是接口類型,因?yàn)榻涌谑且粋€(gè)抽象的定義,而方法卻是具體的實(shí)現(xiàn)。另外,接收器不能是一個(gè)指針類型,但可以說任何其他運(yùn)行類型的指針。
雖然類型與方法使結(jié)構(gòu)體等價(jià)于面向?qū)ο笾械睦郏獹o語(yǔ)言中類型的代碼和綁定在其上的方法的代碼可以不用放置在一起,因此可以存在于不同的源文件中,唯一的要求是必須在同一個(gè)包中。
方法VS函數(shù)
方法和函數(shù)定義區(qū)別在于方法的實(shí)例可以接受參數(shù),編譯器以此確定方法所屬的類型。其它語(yǔ)言中,盡管沒有顯式地定義但也會(huì)在調(diào)用時(shí)隱式地傳遞this實(shí)例參數(shù)。
Go語(yǔ)言中函數(shù)是不屬于任何結(jié)構(gòu)體、類型的方法,函數(shù)是沒有接收器的,而方法則是具有接收器的,因此方法要么屬于一個(gè)結(jié)構(gòu)體,要么屬于一個(gè)新定義的類型。
Go語(yǔ)言中相同名字的方法可以定義在不同的類型上,但相同名字的函數(shù)是不被允許的。
由于方法本質(zhì)上是函數(shù),因此不允許方法重載,對(duì)于一個(gè)類型只能有一個(gè)給定名稱的方法。若基于接收器的類型,則可以重載。具有相同名字的方法可以在不同的接收器類型上存在,比如同一個(gè)包中。
面向?qū)ο笳Z(yǔ)言中類擁有的方法表示類所具有的行為,在Go語(yǔ)言中方法也是如此,只是Go語(yǔ)言建立的接收器會(huì)強(qiáng)調(diào)方法的作用對(duì)象是接收器即類的實(shí)例,而函數(shù)是沒有作用對(duì)象的。
| 方法 | 函數(shù) |
|---|---|
| 包含接收器 | 不包含接收器 |
| 可接受指針和值 | 禁止同時(shí)接受指針和值 |
| 可定義同名非同類型的方法 | 禁止定義同名非同類型的函數(shù) |
接收器
方法的接收器支持值接收器和指針接收器,二者的區(qū)別在于指針接收器方法內(nèi)部的改變對(duì)于調(diào)用者是可見的,值接收器則不可見。
func (接收器變量 接收器類型) 方法名(參數(shù)列表) (返回參數(shù)) {
方法體
}
- 接收器變量
接收器中的參數(shù)變量命名時(shí),官方建議使用接收器類型名稱的首字母的小寫,而非self、this之類的命名。 - 接收器類型
接收器類型和參數(shù)類似,可以是指針類型或非指針類型 - 方法名、參數(shù)列表、返回值參數(shù)與函數(shù)定義相同
Go語(yǔ)言中允許定義接收者為結(jié)構(gòu)體類型的方法,方法內(nèi)部可訪問接收器字段。
package main
type User struct {
Name string
Salary int
}
func (receiver User) test() {
println(receiver.Name)
}
func main() {
user := User{Name:"admin", Salary: 1000}
user.test()
}
Go語(yǔ)言中只要類型和方法定義在同一個(gè)包中,即可使用非結(jié)構(gòu)體類型接收器創(chuàng)建方法。若存在int、string等不同的包中,則編譯器會(huì)拋出錯(cuò)誤。
package main
type integer int
func (receiver integer) add(value integer) integer{
return receiver + value
}
func main() {
v1 := integer(1)
v2 := integer(2)
result := v1.add(v2)
println(result)//3
}
Go語(yǔ)言中允許使用指針接收器創(chuàng)建方法,在指針接收器的作用下,對(duì)方法中所做的更改將反映到調(diào)用方中。
func (ptr *Type) method_name(...Type) Type {
}
package main
type User struct {
Id int
Name string
}
//使用User類型的接收者
func (this *User) SetName(name string){
(*this).Name = name
}
func main() {
//初始化結(jié)構(gòu)體
user := User{Id:1, Name:"admin"}
//創(chuàng)建指針
ptr := &user
//使用指針調(diào)用方法
ptr.SetName("root")
println(user.Name)//root
println(ptr.Name)//root
}
方法可以接受指針和值
Go語(yǔ)言中當(dāng)一個(gè)函數(shù)具有值參數(shù)時(shí)僅會(huì)接受參數(shù)的值,若將指針傳遞給值函數(shù)則不會(huì)被接受,反之亦然。但Go語(yǔ)言的方法可以接受值和指針,無論是使用指針還是值接收器定義的。
package main
type User struct {
Id int
Name string
}
//帶指針的方法
func (this *User) SetName(name string){
(*this).Name = name
}
//帶值得方法
func (this User) GetName(){
println(this.Name)
}
func main() {
//初始化值
user := User{Id:1, Name:"admin"}
user.SetName("root")
user.GetName()//root
(&user).GetName()//root
}
值接收器
當(dāng)方法作用于非指針接收器時(shí),Go語(yǔ)言會(huì)在代碼運(yùn)行時(shí)將接收器的值復(fù)制一份,在非指針接收器的方法中可以獲取接收器的成員值,但修改后無效。
當(dāng)使用值接收器聲明的方法,調(diào)用時(shí)會(huì)使用值的副本來執(zhí)行,因此該類型的值并不會(huì)被改變。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = admin
}
使用指針來調(diào)用使用值接收器聲明的方法時(shí),指針被解引為值的副本,因此原指針變量指向的值不會(huì)發(fā)生改變。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := &User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = admin
}
指針接收器
指針類型的接收器由結(jié)構(gòu)體的指針組成,更接近于面向?qū)ο笾械膖his或self。
由于指針的特性,調(diào)用方法時(shí),修改接收器指針的成員變量,在方法結(jié)束后修改都是有效i的。
使用類型的指針調(diào)用指針接收器聲明的方法時(shí),該方法會(huì)共享指針指向的值,因此會(huì)改變指針指向的?。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this *User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := &User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = root
}
適用類型的值調(diào)用指針接收器聲明的方法時(shí),該方法會(huì)共享所指向的值,此時(shí)會(huì)改變指針指向的值。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this *User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = root
}