GO語言的interface

ss
5 min readSep 23, 2018

--

最近工作有關,需要看kubernetes的source code,而我們也必須從go語言著手,畢竟k8s是用go寫的大型專案,筆者比較常用c跟python,go的這個interface讓我覺得很困難,(對岸翻譯是接口)
於是寫個紀錄一下

什麼事interface,他有兩個身份,一個為methods的集合,令一個為它屬於一種型態(type)

這樣聽起來還是一樣模糊?對的在我眼裡看來也是,我們稍微舉個例子,幫助消化

譬如說,我們想要表達一個人的年紀大於50歲為老人,我們可以用


func Old(a int) bool{
return a > 50
}
func main(){
age := 30
fmt.Printf(“the main is old?%t%n”,Old(age))
}

這樣看起來也是很好理解,但可能當變數一多,讓我們對Old這個func無法輕易了解他的參數到底是代表什麼,但當我們多加了一個type的方式去看


type Age int
func(a Age)Old() bool{
return a > 50
}
func main(){
var a Age = 20
fmt.Printf(“the main is old?%t%n”,a.Old())
}

注意到type不僅是類似c/c++ typedef的功能,你會發現這樣的用法也很類似物件導向method的感覺,這邊簡單的舉例一下type的特性,因為後面的interface就具有type的特性

下面我們可以常常看到一種例子,在我們初學interface的時候類似的例子

type Coder interface{
Speak() string
}
type PHP struct{}
type Java struct{}
type C struct{}
type Go struct{}
type Python struct{}
func (h PHP) Speak() string{
return “PHP is the best”//?
}
func (j Jata)Speak() string{
return “Java is cool”
}
func (c C)Speak() string{
return “C is old”
}
func (g Go)Speak() string{
return “GO GO GO”
}
func (p Python)Speak() string{
return “faster”
}
func main(){
coder:=[]Coder{PHP{},Java{},C{},Go{},Python{}}
for _,man := range coder{
fmt.Println(man.Speak())
}
}

則每個人會把每個人的話都說出來,注意到這邊的人都必須有個特性,就是他們都必須有一個func 名為Speak否則,他不會被加進coder的數列裡,所以我們也可以說這些人都是Coder,若不清楚可以試著註解掉一個func Speak,就無法順利執行

到這邊,就能了解interface的功能了,但我困惑的是常常看見 <em> interface{}</em> 這個東西,瀏覽一下找到一個很棒的教學解釋,
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go
並在這邊在紀錄一下

文裡面舉出一個例子

func DoSomething(v interface{}) {
// …
}

在這個func 裡,傳入的參數為v interface{},說明這個參數的型態就是interface{},這樣搞不懂?沒關係我也花了很大的功夫去了解

先解釋一下interface{}由於裡面的method是空的,所以相對的它實現了所有type,不懂的我們可以看到上面coder的例子,唯有type有實做Speak這個func,它才會實現coder這個interface,換句話說,會說話(func)才有資格當coder,有人說它就像是c語言裡面(void*)的角色一樣,或許熟悉c的人可能會更輕易的了解,也因為它不包含任何method,我們可以利用這個空interface來存任意類型的數值

用文中的範例來看

package mainimport (
“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)
nums := []int{1, 2, 3, 4, 5}
vals2 := make([]interface{}, len(nums))
for i, v := range nums {
vals2[i] = v
}
PrintAll(vals2)
}

我們可以看到這個奇妙的用法,我們在定義func PrintAll的時候沒有告訴使用者,這個vals的型態是什麼,所以無論是任意型態的slice我們都可以用這個func,但在那之前要記得轉型成interface{}(不懂可以看一下上面操作)

雖然看似很好用很方便,但這種作法是好是壞,就看你怎麼決定了,畢竟,沒有決定好型態的方式有時會出現我們意想不到的狀況

原來還有這樣的用法,Go這個語言還在讓我大開眼界中,筆者懂它的企圖心跟強大,但它的許多理念比較篇資工,讓其他使用python的使用者要完全跳過來可能還有些難度

--

--

ss
ss

No responses yet