Golang通脈之方法詳情
方法和接收者
Go語言中的方法(Method
)是一種作用于特定類型變量的函數(shù)。這種特定類型變量叫做接收者(Receiver
)。接收者的概念就類似于其他語言中的this或者 self。
Go 語言中同時(shí)有函數(shù)和方法。一個(gè)方法就是一個(gè)包含了接受者的函數(shù),接受者可以是命名類型或者結(jié)構(gòu)體類型的一個(gè)值或者是一個(gè)指針。所有給定類型的方法屬于該類型的方法集
方法只是一個(gè)函數(shù),它帶有一個(gè)特殊的接收器類型,它是在func關(guān)鍵字和方法名之間編寫的。接收器可以是struct類型或非struct類型。接收方可以在方法內(nèi)部訪問。
方法能給用戶自定義的類型添加新的行為。它和函數(shù)的區(qū)別在于方法有一個(gè)接收者,給一個(gè)函數(shù)添加一個(gè)接收者,那么它就變成了方法。接收者可以是值接收者,也可以是指針接收者。
在調(diào)用方法的時(shí)候,值類型既可以調(diào)用值接收者的方法,也可以調(diào)用指針接收者的方法;指針類型既可以調(diào)用指針接收者的方法,也可以調(diào)用值接收者的方法。
也就是說,不管方法的接收者是什么類型,該類型的值和指針都可以調(diào)用,不必嚴(yán)格符合接收者的類型。
方法的定義格式如下:
func (t Type) methodName(parameter)(return) { }
其中
t
:接收者中的參數(shù)變量名在命名時(shí),官方建議使用接收者類型名稱首字母的小寫,而不是self
、this
之類的命名。Type
:接收者類型和參數(shù)類似,可以是指針類型和非指針類型。methodName
、parameter
、return
:具體格式與函數(shù)定義相同。
//Person 結(jié)構(gòu)體 type Person struct { name string age int8 } //NewPerson 構(gòu)造函數(shù) func NewPerson(name string, age int8) *Person { return &Person{ name: name, age: age, } } //Dream Person做夢(mèng)的方法 func (p Person) Dream() { fmt.Printf("%s的夢(mèng)想是學(xué)好Go語言!\n", p.name) } func main() { p1 := NewPerson("張三", 25) p1.Dream() }
方法與函數(shù)的區(qū)別是,函數(shù)不屬于任何類型,方法屬于特定的類型。
可以定義相同的方法名:
type Rectangle struct { width, height float64 } type Circle struct { radius float64 } func (r Rectangle) area() float64 { return r.width * r.height } //該 method 屬于 Circle 類型對(duì)象中的方法 func (c Circle) area() float64 { return c.radius * c.radius * math.Pi } func main() { r1 := Rectangle{12, 2} r2 := Rectangle{9, 4} c1 := Circle{10} c2 := Circle{25} fmt.Println("Area of r1 is: ", r1.area()) fmt.Println("Area of r2 is: ", r2.area()) fmt.Println("Area of c1 is: ", c1.area()) fmt.Println("Area of c2 is: ", c2.area()) }
運(yùn)行結(jié)果:
Area of r1 is: 24
Area of r2 is: 36
Area of c1 is: 314.1592653589793
Area of c2 is: 1963.4954084936207
- 雖然
method
的名字一模一樣,但是如果接收者不一樣,那么method
就不一樣 method
里面可以訪問接收者的字段- 調(diào)用
method
通過.訪問,就像struct里面訪問字段一樣
指針類型的接收者
指針類型的接收者由一個(gè)結(jié)構(gòu)體的指針組成,由于指針的特性,調(diào)用方法時(shí)修改接收者指針的任意成員變量,在方法結(jié)束后,修改都是有效的。這種方式就十分接近于其他語言中面向?qū)ο笾械?code>this或者self
:
type Rectangle struct { width, height int } func (r *Rectangle) setVal() { r.height = 20 } func main() { p := Rectangle{1, 2} s := p p.setVal() fmt.Println(p.height, s.height) }
結(jié)果:
20 2
值類型的接收者
當(dāng)方法作用于值類型接收者時(shí),Go語言會(huì)在代碼運(yùn)行時(shí)將接收者的值復(fù)制一份。在值類型接收者的方法中可以獲取接收者的成員值,但修改操作只是針對(duì)副本,無法修改接收者變量本身。
type Rectangle struct { width, height int } func (r Rectangle) setVal() { r.height = 20 } func main() { p := Rectangle{1, 2} s := p p.setVal() fmt.Println(p.height, s.height) // 2 2 }
什么時(shí)候應(yīng)該使用指針類型接收者
- 需要修改接收者中的值
- 接收者是拷貝代價(jià)比較大的大對(duì)象
- 保證一致性,如果有某個(gè)方法使用了指針接收者,那么其他的方法也應(yīng)該使用指針接收者。
方法和函數(shù)
已經(jīng)有了函數(shù),為什么還要使用方法?
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
方法被轉(zhuǎn)換為一個(gè)函數(shù),而Employee struct
作為參數(shù)傳遞給它。這個(gè)程序也產(chǎn)生了相同的輸出:Salary of Sam Adolf is $5000.。
為什么可以用函數(shù)來寫相同的程序呢?有以下幾個(gè)原因:
Go不是一種純粹面向?qū)ο蟮木幊陶Z言,它不支持類。因此,類型的方法是一種實(shí)現(xiàn)類似于類的行為的方法。
相同名稱的方法可以在不同的類型上定義,而具有相同名稱的函數(shù)是不允許的。
任意類型添加方法
在Go語言中,接收者的類型可以是任何類型,不僅僅是結(jié)構(gòu)體,任何類型都可以擁有方法。 舉個(gè)例子,我們基于內(nèi)置的int類型使用type關(guān)鍵字可以定義新的自定義類型,然后為我們的自定義類型添加方法。
//MyInt 將int定義為自定義MyInt類型 type MyInt int //SayHello 為MyInt添加一個(gè)SayHello的方法 func (m MyInt) SayHello() { fmt.Println("Hello, 我是一個(gè)int。") } func main() { var m1 MyInt m1.SayHello() //Hello, 我是一個(gè)int。 m1 = 100 fmt.Printf("%#v %T\n", m1, m1) //100 main.MyInt }
注意事項(xiàng): 非本地類型不能定義方法,也就是說我們不能給別的包的類型定義方法。
方法繼承
方法是可以繼承的,如果匿名字段實(shí)現(xiàn)了一個(gè)方法,那么包含這個(gè)匿名字段的struct
也能調(diào)用該方法
type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string } type Employee struct { Human //匿名字段 company string } func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } func main() { mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} mark.SayHi() sam.SayHi() }
運(yùn)行結(jié)果:
Hi, I am Mark you can call me on 222-222-YYYY Hi, I am Sam you can call me on 111-888-XXXX
方法重寫
type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string } type Employee struct { Human //匿名字段 company string } //Human定義method func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } //Employee的method重寫Human的method func (e *Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) //Yes you can split into 2 lines here. } func main() { mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} mark.SayHi() sam.SayHi() }
運(yùn)行結(jié)果:
Hi, I am Mark you can call me on 222-222-YYYY
Hi, I am Sam, I work at Golang Inc. Call me on 111-888-XXXX
- 方法是可以繼承和重寫的
- 存在繼承關(guān)系時(shí),按照就近原則,進(jìn)行調(diào)用
結(jié)構(gòu)體和方法補(bǔ)充
因?yàn)?code>slice和map
這兩種數(shù)據(jù)類型都包含了指向底層數(shù)據(jù)的指針,因此在需要復(fù)制它們時(shí)要特別注意:
type Person struct { name string age int8 dreams []string } func (p *Person) SetDreams(dreams []string) { p.dreams = dreams } func main() { p1 := Person{name: "張三", age: 18} data := []string{"吃飯", "睡覺", "打豆豆"} fmt.Printf("%p\n",data) //0xc00006e360 p1.SetDreams(data) //傳的是 data 的內(nèi)存地址,此時(shí)p.dreams和data指向同一塊內(nèi)存空間 fmt.Printf("%p\n",p1.dreams) //0xc00006e360 // 你真的想要修改 p1.dreams 嗎? data[1] = "不睡覺" //data值的修改會(huì)影響person結(jié)構(gòu)體的dream字段 fmt.Println(p1.dreams) // [吃飯 不睡覺 打豆豆] }
正確的做法是在方法中使用傳入的slice
的拷貝進(jìn)行結(jié)構(gòu)體賦值。
func (p *Person) SetDreams(dreams []string) { p.dreams = make([]string, len(dreams)) copy(p.dreams, dreams) }
同樣的問題也存在于返回值slice
和map
的情況,在實(shí)際編碼過程中一定要注意這個(gè)問題。
到此這篇關(guān)于Golang通脈方法詳情的文章就介紹到這了,更多相關(guān)Golang通脈方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go redis實(shí)現(xiàn)滑動(dòng)窗口限流的方式(redis版)
這篇文章主要介紹了go redis實(shí)現(xiàn)滑動(dòng)窗口限流的方式(redis版),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Go語言標(biāo)準(zhǔn)庫(kù)sync.Once使用場(chǎng)景及性能優(yōu)化詳解
這篇文章主要為大家介紹了Go語言標(biāo)準(zhǔn)庫(kù)sync.Once使用場(chǎng)景及性能優(yōu)化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Golang 操作TSV文件的實(shí)戰(zhàn)示例
本文主要介紹了Golang 操作TSV文件的實(shí)戰(zhàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03GO語言實(shí)現(xiàn)簡(jiǎn)單的目錄復(fù)制功能
這篇文章主要介紹了GO語言實(shí)現(xiàn)簡(jiǎn)單的目錄復(fù)制功能,通過新建及復(fù)制內(nèi)容等操作最終實(shí)現(xiàn)復(fù)制目錄的功能效果,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12