方法
Go中沒有類,但是可以為結(jié)構(gòu)體定義方法,方法就是一類帶有特殊的接受者參數(shù)的函數(shù)。方法接受者在它自己的參數(shù)列表內(nèi),位于func關(guān)鍵字和方法名之間。例如:
package main
import "fmt"
type Vertex struct{
x,y float64
}
func (v Vertex) Abs() float64{
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
你也可以為非結(jié)構(gòu)體類型聲明方法。但只能為在同一包內(nèi)定義的類型的接收者聲明方法, 而不能為其它包內(nèi)定義的類型(包括 int 之類的內(nèi)建類型)的接收者聲明方法。也就是說接收者的定義與方法的聲明必須在同一包內(nèi),且不能為內(nèi)建類型聲明方法。
指針接收者
在Go中可以為指針接收者定義方法,對(duì)于某個(gè)類型T的接收者的類型可以使用T文法(T不能是像int之類的指針)。指針接收者的方法可以修改接收者指向的值。由于方法經(jīng)常需要修改它的接收者,指針接收者比值接收者更常用。
帶指針參數(shù)的函數(shù)必須接受一個(gè)指針,而以指針為接收者的方法被調(diào)用時(shí),接收者既能為值又能為指針。接受一個(gè)值作為參數(shù)的函數(shù)必須接受一個(gè)指定類型的值,而以值為接收者的方法被調(diào)用時(shí),接收者既能為值又能為指針。
在開發(fā)中建議選擇指針作為接收者,這樣做有兩個(gè)好處:
- 方法可以直接修改接收者的值
- 這樣可以避免在每次調(diào)用方法時(shí)復(fù)制該值。若值的類型為大型結(jié)構(gòu)體時(shí),這樣做會(huì)更加高效
接口
接口是由一組方法簽名定義的集合,接口類型的值可以保存任何實(shí)現(xiàn)了接口方法的變量。類型通過實(shí)現(xiàn)了一個(gè)接口的所有方法來實(shí)現(xiàn)這個(gè)接口,而不需要專門的顯示聲明也就是"implements"關(guān)鍵字來聲明。隱式接口從接口的實(shí)現(xiàn)中解耦了定義,這樣接口的實(shí)現(xiàn)可以出現(xiàn)在任何包中,無需提前準(zhǔn)備。
在內(nèi)部,接口的值可以看做是包含值和具體類型的元組:
( value , type )
接口的值保存了一個(gè)具體底層類型的具體值,接口值調(diào)用方法時(shí)會(huì)調(diào)用具體底層類型的同名方法。在Go中即使接口值的底層值是nil,方法仍然會(huì)被nil的接收者調(diào)用。
package main
import "fmt"
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"hello"}
describe(i)
i.M()
}
func describe(i I) {
fmt.Printf("(%v, %T)\n", i, i)
}
//輸出結(jié)果
//(<nil>, *main.T)
//<nil>
//(&{hello}, *main.T)
//hello
而nil接口值不保存值也不保存具體類型。為nil接口調(diào)用方法會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤,因?yàn)榻涌诘脑M內(nèi)并未包含能夠指明該調(diào)用哪個(gè) 具體 方法的類型。
指定了零個(gè)方法的接口被稱為空接口,即:
interface{}
空接口可以保存任意類型的值,空接口一般是用來處理位置類型的值。
類型斷言提供了訪問接口值底層具體值的方法。
//斷言i是T類型的值,并返回i的底層值返回給t
t := i.(T)
//斷言i是V類型的值,若i是V類型的值ok的為true,v為i的底層值,否則ok為false,v為零值。
v,ok := i.(V)
以上就是接口斷言的兩種方式,第一種會(huì)觸發(fā)warning提示,第二種則不會(huì)觸發(fā)。因?yàn)榈诙N方式判斷了i是否保存了T類型的值。
類型選擇是一種按順序從幾個(gè)類型語言中選擇分支的結(jié)構(gòu)。與switch相似,不同的是類型選擇中的case為類型。聲明與類型斷言相似,但是括號(hào)內(nèi)由具體類型修改為type關(guān)鍵字。
package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}
Stringer
fmt包中定義的Stringer是最普遍的接口之一。
type Stringer interface { String() string}
Stringer是一個(gè)可以用字符串描述自己的類型。fmt包(還有很多包)都通過此接口來打印值,Stringer的功能有點(diǎn)類似于java中的toString。