struct的方法,如果receiver非指標,則呼叫這個方法無法改變物件狀態,因為傳遞給方法的物件是原物件的一個拷貝,所有的改變都被作用在這個拷貝上而非原物件上.
type st struct{
val uint32
}
func (this st) Show(){
fmt.Printf("Show:%d
",this.val)
}
func (this st) Increase(){
this.val += 1
fmt.Printf("Increase:%d
",this.val)
}
func main(){
b := st{val:10}
b.Increase()
b.Show()
}
輸出:
Increase:11
Show:10
對於習慣了C++的程式設計師,總會認為呼叫一個物件的非const方法是可以改變那個物件的內部狀態.但是這種思維如果延續到go中將會導致很難查詢的bug.
到底是物件實現了介面還是指向物件的指標實現了介面
先來看以下程式碼:
package main
import "fmt"
type intf interface{
Show()
}
type st struct{
val uint32
}
func (this *st) Show(){
this.val += 1
fmt.Printf("%d
",this.val)
}
func main(){
var a intf
b := st{val:10}
a = b
a.Show()
}
直觀上我們認為st實現了intf介面,所以可以用b對a賦值,而實際上執行這段程式碼將會報錯:
# command-line-arguments
test/test.go:21: cannot use b (type st) as type intf in assignment:
st does not implement intf (Show method has pointer receiver)
這段提示說st沒有實現intf介面,因為Show方法的receiver是一個指標.對程式碼稍作修改:
func main(){
var a intf
b := &st{val:10}
a = b
a.Show()
}
這次程式碼可以正確執行了,此時b應該是一個指向st的指標.這是說*st實現了intf介面?
現在我再把Show的定義改一下,main不變:
func (this st) Show(){
this.val += 1
fmt.Printf("%d
",this.val)
}
程式碼還是可以正確執行,同時如果把main恢復成下面這樣:
func main(){
var a intf
b := st{val:10}
a = b
a.Show()
}
程式碼也是正確的.
也就是說,實現介面的方法時receiver是指標那麼只能用實現型別的指標對介面賦值,否則既能用實現型別的值也能用實現型別的指標對介面賦值.
最後再來看個例子,在一段網路程式碼中,我想將net.Conn轉換成net.TCPConn,如下程式碼報錯:
tcpconn := this.Conn.(net.TCPConn)
# kendynet-go/socket
./tcpsession.go:156: impossible type assertion:
net.TCPConn does not implement net.Conn (Close method has pointer receiver)
Makefile:2: recipe for target `all` failed
正確的做法是:
tcpconn := this.Conn.(*net.TCPConn)
這是否證明了,是*net.TCPConn而不是net.TCPConn實現了net.Conn介面,