如Go method中提及,Golang沒有明確支持多態,但是通過其他手段可以實現類似C++中的多態特性,即本文中即將介紹的Go interface功能。
interface定義
interface(接口)是golang最重要的特性之一,Interface類型可以定義一組方法,但是這些不需要實現。請注意:此處限定是一組方法,既然是方法,就不能是變量;而且是一組,表明可以有多個方法。再多聲明一點,interface本質上是一種類型,確切的說,是指針類型,此處暫且不多表,後文中自然能體會到。
interface是爲實現多態功能,多態是指代碼可以根據類型的具體實現採取不同行爲的能力。如果一個類型實現了某個接口,所有使用這個接口的地方,都可以支持這種類型的值。
type 接口名稱 interface {
method1(參數列表) 返回值列表
method2(參數列表) 返回值列表
...
methodn(參數列表) 返回值列表
}
接口通常以er作爲名稱後綴,方法名是聲明組成部分,但參數名可不同或省略。如果接口沒有任何方法聲明,那麼就是一個空接口(interface{}),它的用途類似面向對象裏的根類型Object,可被賦值爲任何類型的對象。接口變量默認值是nil。如果實現接口的類型支持,可做相等運算。
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
此外,還可以像匿名字段那樣,嵌入其他接口。目標類型方法集中必須擁有包含嵌入接口方法在內的全部方法纔算實現了該接口。
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應用場景
類型轉換
類型推斷可將接口變量還原爲原始類型,或用來判斷是否實現了某個更具體的接口類型。
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{ // 轉換爲更具體的接口類型
fmt.Println(n)
}
if d2,ok:=x.(data);ok{ // 轉換回原始類型
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
但是此處會觸發panic
,使用ok-idiom模式,即便轉換失敗也不會引發panic。還可用switch語句在多種類型間做出推斷匹配,這樣空接口就有更多發揮空間。
func main() {
var x interface{} =func(x int)string{
return fmt.Sprintf("d:%d",x)
}
switch v:=x.(type) { // 局部變量v是類型轉換後的結果
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
實現多態功能
多態功能是interface實現的重要功能,也是Golang中的一大行爲特色,其多態功能一般要結合Go method實現,作爲函數參數可以容易的實現多臺功能。
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() {
// 創建一個user值並傳給sendNotification
bill := user{"Bill", "[email protected]"}
sendNotification(&bill)
// 創建一個admin值並傳給sendNotification
lisa := admin{"Lisa", "[email protected]"}
sendNotification(&lisa)
}
// sendNotification接受一個實現了notifier接口的值
// 併發送通知
func sendNotification(n notifier) {
n.notify()
}
上述代碼中實現了一個多態的例子,函數sendNotification
接受一個實現了notifier接口的值作爲參數。既然任意一個實體類型都能實現該接口,那麼這個函數可以針對任意實體類型的值來執行notify
方法,調用notify
時,會根據對象的實際定義來實現不同的行爲,從而實現多態行爲。