標籤:go語言 方法
方法是與對象執行個體綁定的特殊函數。
用於維護和展示對象自身的狀態。對象是內斂的。普通函數則專註與演算法流程,通過接受參數來完成特定的邏輯運算,並返回最終結果,方法是有關聯狀態的,函數通常是沒有的。
方法和函數定義文法區別在於前者執行個體接受參數,編譯器以此確定方法所屬的類型。在一些語言中儘管沒有定義,但是函數使用了隱式的傳遞this執行個體參數。
可以為當前包,以及除介面和指標以外的任何類型定義方法。方法同樣不支援重載,receiver參數名沒有限制。不推薦使用this和self。方法可以看做特殊的函數,那麼receiver的類型自然可以是基礎類型或指標。這會關係到調用時對象執行個體是否被複製。
不可以使用多級指標調用方法。
指標類型的receiver必須是合法指標(包括nil),或能擷取執行個體地址。
如何選擇方法的接收器類型:
使用T:
1.不需要修改狀態的小對象或者是固定值。
2.參考型別、字串、函數等指標封裝對象。
使用*T:
1.需要修改執行個體狀態。
2.大對象使用*T,以減少複製成本。
3.如果包含Mutex等同步欄位,用*T,避免因為複製造成鎖操作無效。
4.其他無法確定全部使用*T。
匿名欄位:
方法也會有同名遮蔽問題。但是利用這種特性,可以實作類別似的覆蓋操作。
方法集:
類型有一個與之相關的方法集,這決定它是否實現某個介面。
類型T方法集合包含所有receiver T方法。
類型*T方法集合包含receiver T + *T方法。
匿名嵌入S,T方法集包含所有receiver S方法。
匿名嵌入*S,T方法集包含所有的receiver S + receiver *S方法。
匿名嵌入S或者*S,*T方法集包含所有receiver S + *S。
運算式:
方法可以分為expression和value兩種方法狀態。
(1)方法運算式:
通過類型引用的方法運算式會被還原成為普通函數樣式,接收器是第一個參數,調用時必須顯式傳參。至於類型,可以是T或者是*T,只要目標方法存在於該類型方法集中即可。
(2)方法值:
基於執行個體或者是指標引用的方法值,參數簽名不會改變,依舊按照正常方式調用。但是當方法值被賦值給變數或者是作為參數傳遞時,會立即計算並複製該方法執行所需要的接收器對象,與其綁定,以便在稍後執行時,能隱式傳遞接收器對象。
編譯器會為方法值產生一個封裝函數,實現間接調用。至於接收器複製。和閉包的實現方法基本相同,打包成funval,經由DX寄存器傳送。當然,如果目標方法的接收器是指標類型,那麼被複製的僅僅是指標。只要是接收器參數類型正確,使用nil同樣可以執行。
package main
import "fmt"
type N int
func main() {
var number N = 100
result := number.toString()
fmt.Println(result)//d
}
func (number N)toString() string{
return fmt.Sprintf("%s",string(number))
}
運行結果:
d
package main
import "fmt"
type N int
func main() {
var a N = 25
a.value()
a.pointer()
fmt.Printf("a: %p, %v", &a, a)
}
func (n N) value() {
n++
fmt.Printf("v: %p, %v\n", &n, n)
}
func (n *N) pointer() {
(*n)++
fmt.Printf("p: %p, %v\n", n, *n)
}
/*
運行結果:
v: 0xc04204c088, 26
p: 0xc04204c080, 26
a: 0xc04204c080, 2
*/
Go語言之方法詳解