??如Go method中提及,Golang沒有明確支持多態(tài)统阿,但是通過其他手段可以實現(xiàn)類似C++中的多態(tài)特性玷氏,即本文中即將介紹的Go interface功能。
interface定義
??interface(接口)是golang最重要的特性之一摔蓝,Interface類型可以定義一組方法赂苗,但是這些不需要實現(xiàn)。請注意:此處限定是一組方法贮尉,既然是方法拌滋,就不能是變量;而且是一組猜谚,表明可以有多個方法败砂。再多聲明一點,interface本質(zhì)上是一種類型魏铅,確切的說昌犹,是指針類型,此處暫且不多表沦零,后文中自然能體會到祭隔。
??interface是為實現(xiàn)多態(tài)功能,多態(tài)是指代碼可以根據(jù)類型的具體實現(xiàn)采取不同行為的能力路操。如果一個類型實現(xiàn)了某個接口疾渴,所有使用這個接口的地方,都可以支持這種類型的值屯仗。
type 接口名稱 interface {
method1(參數(shù)列表) 返回值列表
method2(參數(shù)列表) 返回值列表
...
methodn(參數(shù)列表) 返回值列表
}
??接口通常以er作為名稱后綴搞坝,方法名是聲明組成部分,但參數(shù)名可不同或省略魁袜。如果接口沒有任何方法聲明桩撮,那么就是一個空接口(interface{}),它的用途類似面向?qū)ο罄锏母愋蚈bject峰弹,可被賦值為任何類型的對象店量。接口變量默認值是nil。如果實現(xiàn)接口的類型支持鞠呈,可做相等運算融师。
func main() {
var t1,t2 interface{}
println(t1==nil,t1==t2)
t1,t2=100,100
println(t1==t2)
t1,t2=map[string]int{},map[string]int{}
println(t1==t2)
}
輸出為
true true
true
panic:runtime error:comparing uncomparable type map[string]int
??此外,還可以像匿名字段那樣蚁吝,嵌入其他接口旱爆。目標類型方法集中必須擁有包含嵌入接口方法在內(nèi)的全部方法才算實現(xiàn)了該接口舀射。
type stringer interface{
string()string
}
type tester interface{
stringer // 嵌入其他接口
test()
}
type data struct{}
func(*data)test() {
}
func(data)string()string{
return""
}
func main() {
var d data
var t tester= &d
t.test()
println(t.string())
}
interface應(yīng)用場景
類型轉(zhuǎn)換
??類型推斷可將接口變量還原為原始類型,或用來判斷是否實現(xiàn)了某個更具體的接口類型怀伦。
type data int
func(d data)String()string{
return fmt.Sprintf("data:%d",d)
}
func main() {
var d data=15
var x interface{} =d
if n,ok:=x.(fmt.Stringer);ok{ // 轉(zhuǎn)換為更具體的接口類型
fmt.Println(n)
}
if d2,ok:=x.(data);ok{ // 轉(zhuǎn)換回原始類型
fmt.Println(d2)
}
e:=x.(error) // 錯誤:main.data is not error
fmt.Println(e)
}
輸出為:
data:15
data:15
panic:interface conversion:main.data is not error:missing method Error
??但是此處會觸發(fā)panic
脆烟,使用ok-idiom模式,即便轉(zhuǎn)換失敗也不會引發(fā)panic房待。還可用switch語句在多種類型間做出推斷匹配邢羔,這樣空接口就有更多發(fā)揮空間。
func main() {
var x interface{} =func(x int)string{
return fmt.Sprintf("d:%d",x)
}
switch v:=x.(type) { // 局部變量v是類型轉(zhuǎn)換后的結(jié)果
case nil:
println("nil")
case*int:
println(*v)
case func(int)string:
println(v(100))
case fmt.Stringer:
fmt.Println(v)
default:
println("unknown")
}
}
輸出為:
d:100
實現(xiàn)多態(tài)功能
??多態(tài)功能是interface實現(xiàn)的重要功能吴攒,也是Golang中的一大行為特色张抄,其多態(tài)功能一般要結(jié)合Go method實現(xiàn),作為函數(shù)參數(shù)可以容易的實現(xiàn)多臺功能洼怔。
package main
import "fmt"
// notifier是一個定義了通知類行為的接口
type notifier interface {
notify()
}
// 定義user及user.notify方法
type user struct {
name string
email string
}
func (u *user) notify() {
fmt.Printf("Sending user email to %s<%s>\n",
u.name,
u.email)
}
// 定義admin及admin.notify方法
type admin struct {
name string
email string
}
func (a *admin) notify() {
fmt.Printf("Sending admin email to %s<%s>\n",
a.name,
a.email)
}
func main() {
// 創(chuàng)建一個user值并傳給sendNotification
bill := user{"Bill", "bill@email.com"}
sendNotification(&bill)
// 創(chuàng)建一個admin值并傳給sendNotification
lisa := admin{"Lisa", "lisa@email.com"}
sendNotification(&lisa)
}
// sendNotification接受一個實現(xiàn)了notifier接口的值
// 并發(fā)送通知
func sendNotification(n notifier) {
n.notify()
}
??上述代碼中實現(xiàn)了一個多態(tài)的例子,函數(shù)sendNotification
接受一個實現(xiàn)了notifier接口的值作為參數(shù)左驾。既然任意一個實體類型都能實現(xiàn)該接口镣隶,那么這個函數(shù)可以針對任意實體類型的值來執(zhí)行notify
方法,調(diào)用notify
時诡右,會根據(jù)對象的實際定義來實現(xiàn)不同的行為安岂,從而實現(xiàn)多態(tài)行為。