在 Go 中,類型可以定義接收此類型的函數,即方法。每個類型都有接口,意味着對那個類型定義了方法集合。
下面定義了結構體類型 S 以及它的兩個方法:
type S struct { i int }
func (p *S) Get() int { return p.i }
func (p *S) Put(v int) { p.i = v }
方法
方法就是有接收者的函數。
可以在除了非本地類型(包括內建類型,比如 int)的任意類型上定義方法。然而可以爲內建類型定義別名,然後就可以爲別名定義方法。如
type Foo int // 爲 int 定義別名 Foo
func (self Foo) Emit() {
fmt.Printf("%v", self)
}
接口
接口定義爲一個方法的集合。方法包含實際的代碼。換句話說,一個接口就是定義,而方法就是實現。因此,接收者不能定義爲接口類型,這樣做的話會引起 invalid receiver type … 的編譯器錯誤。
來自語言說明書的權威內容:
接收者類型必須是T 或*T,這裏的 T 是類型名。 T 叫做接收者基礎類型或簡稱基礎類型。基礎類型一定不能是指針或接口類型,並且定義在與方法相同的包中。
// 定義了有兩個方法的接口 I,結構 S 實現了此接口
type I interface {
Get() int
Put( int)
}
可以定義接口類型的變量(接口值)作爲函數的參數,實現了此接口的類型的變量可以作爲實參傳遞給該接口變量並調用其方法。
func f(p I) {
fmt.Println(p.Get())
p.Put(1)
}
var s S
f(&s) // S 類型的變量 s 作爲實參傳給接口。取地址是因爲在 s 的指針上定義了方法,這樣可以修改 s;
// 如果在 s 上定義方法,則修改的只是 s 的副本
在 Go 中創建指向接口的指針是無意義的。創建接口值的指針也是非法的。
接口類型判斷
在 Go 中,要判斷傳遞給接口值的變量類型,可以在使用 type switch 得到。(type)
只能在 switch
中使用。
// 另一個實現了 I 接口的 R 類型
type R struct { i int }
func (p *R) Get() int { return p.i }
func (p *R) Put(v int) { p.i = v }
func f(p I) {
switch t := p.(type) { // 判斷傳遞給 p 的實際類型
case *S: // 指向 S 的指針類型
case *R: // 指向 R 的指針類型
case S: // S 類型
case R: // R 類型
default: //實現了 I 接口的其他類型
}
}
若要在 switch
外判斷一個接口類型是否實現了某個接口,可以使用“逗號 ok ”。
value, ok := Interfacevariable.(implementType)
其中 Interfacevariable
是接口變量(接口值),implementType
爲實現此接口的類型,value
返回接口變量實際類型變量的值,如果該類型實現了此接口返回 true
。
type I interface{ // 有一個方法的接口 I
Get() Int
}
type Int int // Int 類型實現了 I 接口
func (i Int) Get() Int{
return i
}
var myint Int = 5
var inter I = myint // 變量賦值給接口
val, ok := inter.(Int)
fmt.Printf("%v, %v", val, ok) // 輸出爲:5,true
空接口
每種類型都能匹配到空接口:interface{}
。空接口類型對方法沒有任何約束(因爲沒有方法),它能包含任意類型,也可以實現到其他接口類型的轉換。如果傳遞給該接口的類型變量實現了轉換後的接口則可以正常運行,否則出現運行時錯誤。
func g(si interface{}) int {
return si.(I).Get()
}
s = new(S)
g(s) // (I) 將 si 轉換到 I 類型的接口,s 實現了 Get 方法,因此可以調用 g()
i := 5
g(i) // 運行時出錯,因爲 int 型沒有 Get 方法
還可以在接口中列出另一個接口,如
type Interface interface {
sort.Interface // 另一個接口
Push(x interface{})
Pop() interface{}
}
自省和反射
…