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語言之方法詳解