Go語言的Interface接口是一種神奇的特性逾一。Interface包括了一組方法,同時(shí)也是一種類型肮雨。Interface支持"鴨子類型"(Duck
Typing),只要能做所有鴨子能做的事遵堵,就認(rèn)為它是個(gè)鴨子;只要實(shí)現(xiàn)了接口的所有方法,就是這個(gè)Interface的類型怨规。
type Animal interface {
Speak() string
}
type Dog struct {
}
func (d Dog) Speak() string {
return "Woof!"
}
type Cat struct {
}
func (c Cat) Speak() string {
return "Meow!"
}
type Llama struct {
}
func (l Llama) Speak() string {
return "?????"
}
type JavaProgrammer struct {
}
func (j JavaProgrammer) Speak() string {
return "Design patterns!"
}
func main() {
animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
1. 空接口 interface{}
interface{}作為類型時(shí)可以看做是一個(gè)實(shí)現(xiàn)了0個(gè)方法的匿名接口陌宿,任何類型都至少實(shí)現(xiàn)了0個(gè)方法,因此任何類型都可以實(shí)現(xiàn)了interface{}接口椅亚。有這樣一個(gè)有趣的錯(cuò)誤的例子限番。
// 這是錯(cuò)誤的
package main
import (
"fmt"
)
func PrintAll(vals []interface{}) {
for _, val := range vals {
fmt.Println(val)
}
}
func main() {
names := []string{"stanley", "david", "oscar"}
PrintAll(names)
}
因?yàn)閇]interface{}類型是一種普通類型,即元素為interface{}的切片類型呀舔,而不是一個(gè)接口。如果要轉(zhuǎn)換的話,需要對每個(gè)元素進(jìn)行類型轉(zhuǎn)換媚赖,代碼如下:
package main
import (
"fmt"
)
func PrintAll(vals []interface{}) {
for _, val := range vals {
fmt.Println(val)
}
}
func main() {
names := []string{"stanley", "david", "oscar"}
vals := make([]interface{}, len(names))
for i, v := range names {
vals[i] = v
}
PrintAll(vals)
}
2. 接口和指針
如果第一個(gè)例子的Cat的speak方法做一個(gè)改成如下的代碼:
func (c *Cat) Speak() string {
return "Meow!"
}
再運(yùn)行程序就會報(bào)錯(cuò)霜瘪。
在go語言中,如果是指針作為receiver惧磺,當(dāng)使用值作為該接口的實(shí)例調(diào)用會出現(xiàn)編譯錯(cuò)颖对。而相反的,使用值作為receiver磨隘,使用指針作為接口實(shí)例調(diào)用沒有問題缤底。
為什么呢?
因?yàn)橥ㄟ^一個(gè)指針番捂,就可以訪問這個(gè)類型作為receiver的所有方法个唧。但是返回來,給你一個(gè)值设预,卻不能訪問這個(gè)值的指針類型的方法徙歼。
在進(jìn)一步解釋就是: Go語言的所有傳遞都是值傳遞的。在將Cat轉(zhuǎn)換為Animal的Interface的時(shí)候鳖枕,傳遞的是Cat的值拷貝魄梯,取不到原來的指針,此時(shí)取指針的話宾符,取到的就不是原來的Cat的指針酿秸,就失去了以指針作為reciever的意義了。而反過來魏烫,通過一個(gè)指針的值辣苏,可以找到唯一的一個(gè)Cat對象,所以是可以轉(zhuǎn)換的则奥。
如果了解了考润,go語言如何實(shí)現(xiàn)interface的,interface的數(shù)據(jù)結(jié)構(gòu)分為兩個(gè)部分:類型和方法列表读处,原來的數(shù)據(jù)本身糊治。就更好理解了。
3. 接口的類型轉(zhuǎn)換
見我的另一篇《Go語言類型轉(zhuǎn)換和類型斷言》http://www.reibang.com/p/bd2acab2a8e9
4. Embedding(嵌入)
Go中沒有繼承的概念罚舱,用Embedding來取代井辜,實(shí)現(xiàn)方式為接口的匿名成員。
見《Go匿名成員》 http://www.reibang.com/p/76eac0b8d563
參考: