可以先看:go的數據類型
下面是針對不同類型,方法如何定義和使用。
1、方法定義
go可以為任意類型定義方法!!【指針類型除外】
方法比函數的好處:方法名可以簡短。當我們在包外調用的時候這種好處就會被放大,因為我們可以使用這個短名字,而可以省略掉包的名字。
在函數聲明時,在其名字之前放上一個變量
,即是一個方法。這個附加的參數會將該函數附加到這種類型上,即相當於為這種類型定義了一個獨占的方法。
(1)兩種接收器聲明方式:
接收器是類型對象
:
func (p Point) ScaleBy(factor float64) {xx}
接收器是類型指針對象
:
一般會約定如果Point這個類有一個指針作為接收器的方法
,那么所有Point的方法都必須有一個指針接收器
,即使是那些並不需要這個指針接收器的函數。
func (p *Point) ScaleBy(factor float64) {xx}
(2)指針類型的方法
go不能
直接定義指針類型的方法
!!!說的是下面這種情況,因為接收器放類型和指針都不對:
type intPoint *int
//func (?) addThree() int{
// return
//}
func main(){
tmp := 10
var b intPoint = &tmp
// fmt.Print(b.addThree()) //指針類型的方法addThree沒法定義,當然也就沒法調用
}
但是,我們可以換個方式,用指針變量調用指針對應原類型
的方法。調用方式有2種:
type myInt int
func (i myInt) addOne() myInt {
return i + 1
}
func (i *myInt) addTwo() myInt {
return *i + 2
}
func main(){
//方式1
var a myInt = 2 // a是myInt類型
a.addOne() // a不變
fmt.Println(a) // 2
(&a).addTwo() //取地址即指針。傳指針,a變
fmt.Println(a) // 4
//方式2
b := new(myInt) // b是*myInt類型,指針
*b = 10
// 傳值,b不變
(*b).addOne() // 正常訪問,傳入myInt類型
fmt.Println(*b)
b.addOne() // go自己處理,傳入*myInt自動轉為myInt類型
fmt.Println("b:", *b)
//下面傳入指針,b會變
b.addTwo() // 正常訪問,傳入*myInt類型。
fmt.Println(*b)
(*b).addTwo() // go自己處理,傳入myInt自動轉為*myInt類型
fmt.Println(*b)
}
(3)
如果命名類型T(譯注:用type xxx定義的類型)的所有方法都是用T類型自己來做接收器(而不是*T),那么拷貝這種類型的實例就是安全的;調用他的任何一個方法也就會產生一個值的拷貝。比如time.Duration的這個類型,在調用其方法時就會被全部拷貝一份,包括在作為參數傳入函數的時候。
但是如果一個方法使用指針作為接收器
,你需要避免對其進行拷貝
,因為這樣可能會破壞掉該類型內部的不變性
。比如你對bytes.Buffer對象進行了拷貝,那么可能會引起原始對象和拷貝對象只是別名而已,實際上它們指向的對象是一樣的。緊接着對拷貝后的變量進行修改可能會有讓你有意外的結果。
譯注: 作者這里說的比較繞,其實有兩點:
1 不管你的method的receiver是指針類型還是非指針類型,都是可以通過指針/非指針類型進行調用的,編譯器會幫你做類型轉換。
2 在聲明一個method的receiver該是指針還是非指針類型時,你需要考慮兩方面的因素,第一方面是這個對象本身是不是特別大,如果聲明為非指針變量時,調用會產生一次拷貝;第二方面是如果你用指針類型作為receiver,那么你一定要注意,這種指針類型指向的始終是一塊內存地址,就算你對其進行了拷貝。熟悉C或者C++的人這里應該很快能明白。//沒懂
參考:
https://blog.csdn.net/u013862108/article/details/105028124
https://docs.hacknode.org/gopl-zh/ch6/ch6-02.html
2、實現接口的表示:接收器不能是指針!!
接收參數person不可以是指針類型,否則不認為是實現了接口