最近工作有關,需要看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 intfunc(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的使用者要完全跳過來可能還有些難度