golang 的接口類型是對具體類型/結構體的行為進行泛化或抽象,接口的特點是靈活。
接口類型指定了一系列的方法,具體類型必須實現(xiàn)所有的方法才能視為該接口的實例。
package main
import (
"fmt"
"strings"
)
// var talker interface {
// talk() string
// }
type talker interface {
talk() string
}
type martain struct{}
func (m martain) talk() string {
return "nack nack"
}
type laser int
func (l laser) talk() string {
return strings.Repeat("pew ", int(l))
}
func shout(t talker) {
louder := strings.ToUpper(t.talk())
fmt.Printf("%v\n", louder)
}
func main() {
tmp := martain{}
shout(tmp) // NACK NACK
shout(laser(3)) // PEW PEW PEW
}
上述代碼聲明了talker接口,該接口有一個talk()方法,有兩個具體類型martain、laser都各自實現(xiàn)了該方法。一個shout()函數(shù)的入?yún)⑹莟alker接口類型的,這兩種具體類型的變量都可以調(diào)用shout(),因為都實現(xiàn)了talker接口。
關于golang的接口類型,書本或者網(wǎng)上都介紹很詳細,今天主要來研究一下空接口。
空接口
函數(shù)入?yún)?/h2>
golang可以使用空接口作為函數(shù)的入?yún)?,表示可接受任意類型的變量?/p>
package main
import "fmt"
type point struct {
x int
y int
}
func show(any interface{}) {
fmt.Println(any)
}
func main() {
show(12)
show("abc")
show(true)
show(point{1, 2})
}
有興趣的小伙伴可以看看go的源碼中對于fmt.Println()函數(shù)的定義,就是空接口作為入?yún)?,因此Println()可以打印任意類型的數(shù)據(jù)。
類型斷言
空接口可以存儲任意類型的變量,當我們需要獲取變量的具體類型時,可通過斷言來動態(tài)地get。
package main
import "fmt"
func main() {
// 注意,把空接口作為任意類型的變量時,
//它本身也是一個變量,需要使用var關鍵字;而不是聲明接口類型的type關鍵字
var any interface{}
any = 12
value, ok := any.(int)
if !ok {
fmt.Println("it's not int type")
return
}
fmt.Printf("%v\n", value) // 輸出:12
}
如果把any賦值為string或者除int的其他任意類型,再執(zhí)行代碼時就會執(zhí)行到if 語句中,然后退出程序。因為any不是int類型的變量,斷言為false,得到的不是我們想要的類型,當然應該中止程序。

斷言還可以是切片、map:
func main() {
var any interface{} = []string{"a", "b", "c"}
value, ok := any.([]string)
if !ok {
fmt.Println("it's not string slice type")
return
}
for i, v := range value {
fmt.Printf("%d %s\n", i, v)
}
any = map[string]int{"zhangsan": 1, "lisi": 2}
value1, ok := any.(map[string]int)
if !ok {
fmt.Println("it's not map[string]int type")
return
}
for k, v := range value1 {
fmt.Printf("%v %v\n", k, v)
}
}
結構體其實和map類似,就不列舉了。
[]interface{}
在go語言中還有這樣的類型[]interface{}。
通過前面的學習,聰明的你再舉一反三,立馬就明白了這個接口切片是任意類型的切片的意思。
可惜答案卻大錯特錯。
func show(any []interface{}) {
for i, v := range any {
fmt.Printf("%d %v\n", i, v)
}
}
func main() {
strArr := []string{"a", "b", "c"}
show(strArr)
}
上面這段代碼甚至在代碼編寫時就報錯了,無法通過編譯,原因是cannot use strArr (variable of type []string) as []interface{} value in argument to show。
以后的使用場景中肯定會遇到很多[]interface{}的場景(尤其是在解析json數(shù)據(jù)時),請務必記住,[]interface{}!=[]anyType{}。
畢竟interface{}已經(jīng)代表了任意類型,包含了任意類型的切片,為什么還要再重復定義一個[]interface{}來表示任意類型的切片呢?
[]interface{}是一個具體的類型。