typeHumanstruct { name string age int phone string}typeStudentstruct {Human//匿名欄位 Human school string loan float32}typeEmployeestruct {Human//匿名欄位 Human company string money float32}//Human 物件實現 Sayhi 方法func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)}// Human 物件實現 Sing 方法func (h *Human) Sing(lyrics string) { fmt.Println("La la, la la la, la la la la la...", lyrics)}//Human 物件實現 Guzzle 方法func (h *Human) Guzzle(beerStein string) { fmt.Println("Guzzle Guzzle Guzzle...", beerStein)}// Employee 過載 Human 的 Sayhi 方法func (e *Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) //此句可以分成多行}//Student 實現 BorrowMoney 方法func (s *Student) BorrowMoney(amount float32) { s.loan += amount // (again and again and...)}//Employee 實現 SpendSalary 方法func (e *Employee) SpendSalary(amount float32) { e.money -= amount // More vodka please!!! Get me through the day!}// 定義 interfacetypeMeninterface {SayHi()Sing(lyrics string)Guzzle(beerStein string)}typeYoungChapinterface {SayHi()Sing(song string)BorrowMoney(amount float32)}typeElderlyGentinterface {SayHi()Sing(song string)SpendSalary(amount float32)}
透過上面的程式碼我們可以知道,interface 可以被任意的物件實現。我們看到上面的 Men interface 被 Human、Student 和 Employee 實現。同理,一個物件可以實現任意多個 interface,例如上面的 Student 實現了 Men 和 YoungChap 兩個 interface。
那麼 interface 裡面到底能存什麼值呢?如果我們定義了一個 interface 的變數,那麼這個變數裡面可以存實現這個 interface 的任意型別的物件。例如上面例子中,我們定義了一個 Men interface 型別的變數 m,那麼 m 裡面可以存 Human、Student 或者 Employee 值。
因為 m 能夠持有這三種類型的物件,所以我們可以定義一個包含 Men 型別元素的 slice,這個 slice 可以被賦予實現了 Men 介面的任意結構的物件,這個和我們傳統意義上面的 slice 有所不同。
讓我們來看一下下面這個例子:
packagemainimport"fmt"typeHumanstruct { name string age int phone string}typeStudentstruct {Human//匿名欄位 school string loan float32}typeEmployeestruct {Human//匿名欄位 company string money float32}//Human 實現 SayHi 方法func (h Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)}//Human 實現 Sing 方法func (h Human) Sing(lyrics string) { fmt.Println("La la la la...", lyrics)}//Employee 過載 Human 的 SayHi 方法func (e Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) }// Interface Men 被 Human,Student 和 Employee 實現// 因為這三個型別都實現了這兩個方法typeMeninterface {SayHi()Sing(lyrics string)}funcmain() { mike :=Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00} paul :=Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100} sam :=Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000} tom :=Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}//定義 Men 型別的變數 ivar i Men//i 能儲存 Student i = mike fmt.Println("This is Mike, a Student:") i.SayHi() i.Sing("November rain")//i 也能儲存 Employee i = tom fmt.Println("This is tom, an Employee:") i.SayHi() i.Sing("Born to be wild")//定義了 slice Men fmt.Println("Let's use a slice of Men and see what happens") x :=make([]Men, 3)//這三個都是不同型別的元素,但是他們實現了 interface 同一個介面 x[0], x[1], x[2] = paul, sam, mikefor _, value :=range x{ value.SayHi() }}
透過上面的程式碼,你會發現 interface 就是一組抽象方法的集合,它必須由其他非 interface 型別實現,而不能自我實現, Go 透過 interface 實現了 duck-typing:即"當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子"。
packagemainimport ("fmt""strconv")typeHumanstruct { name string age int phone string}// 透過這個方法 Human 實現了 fmt.Stringerfunc (h Human) String() string {return"❰"+h.name+" - "+strconv.Itoa(h.age)+" years - ✆ "+h.phone+"❱"}funcmain() { Bob :=Human{"Bob", 39, "000-7777-XXX"} fmt.Println("This Human is : ", Bob)}
Go 語言裡面有一個語法,可以直接判斷是否是該型別的變數: value, ok = element.(T),這裡 value 就是變數的值,ok 是一個 bool 型別,element 是 interface 變數,T 是斷言的型別。
如果 element 裡面確實儲存了 T 型別的數值,那麼 ok 回傳 true,否則回傳 false。
讓我們透過一個例子來更加深入的理解。
packagemainimport ("fmt""strconv" )typeElementinterface{}typeList [] ElementtypePersonstruct { name string age int }//定義了 String 方法,實現了 fmt.Stringerfunc (p Person) String() string {return"(name: "+ p.name +" - age: "+strconv.Itoa(p.age)+" years)" }funcmain() { list :=make(List, 3) list[0] =1// an int list[1] ="Hello"// a string list[2] =Person{"Dennis", 70}for index, element :=range list {if value, ok := element.(int); ok { fmt.Printf("list[%d] is an int and its value is %d\n", index, value) } elseif value, ok := element.(string); ok { fmt.Printf("list[%d] is a string and its value is %s\n", index, value) } elseif value, ok := element.(Person); ok { fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) } else { fmt.Printf("list[%d] is of a different type\n", index) } } }
是不是很簡單啊,同時你是否注意到了多個 if 裡面,還記得我前面介紹流程時講過,if 裡面允許初始化變數。
也許你注意到了,我們斷言的型別越多,那麼 if else 也就越多,所以才引出了下面要介紹的 switch。
switch 測試
最好的講解就是程式碼例子,現在讓我們重寫上面的這個實現
packagemainimport ("fmt""strconv" )typeElementinterface{}typeList [] ElementtypePersonstruct { name string age int }//列印func (p Person) String() string {return"(name: "+ p.name +" - age: "+strconv.Itoa(p.age)+" years)" }funcmain() { list :=make(List, 3) list[0] =1//an int list[1] ="Hello"//a string list[2] =Person{"Dennis", 70}for index, element :=range list{switch value := element.(type) {caseint: fmt.Printf("list[%d] is an int and its value is %d\n", index, value)casestring: fmt.Printf("list[%d] is a string and its value is %s\n", index, value)casePerson: fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)default: fmt.Println("list[%d] is of a different type", index) } } }
typeInterfaceinterface {sort.Interface//嵌入欄位 sort.InterfacePush(x interface{}) //a Push method to push elements into the heapPop() interface{} //a Pop elements that pops elements from the heap}
typeInterfaceinterface {// Len is the number of elements in the collection.Len() int// Less returns whether the element with index i should sort// before the element with index j.Less(i, j int) bool// Swap swaps the elements with indexes i and j.Swap(i, j int)}
tag := t.Elem().Field(0).Tag //取得定義在 struct 裡面的標籤name := v.Elem().Field(0).String() //取得儲存在第一個欄位裡面的值
取得反射值能回傳相應的型別和數值
var x float64=3.4v := reflect.ValueOf(x)fmt.Println("type:", v.Type())fmt.Println("kind is float64:", v.Kind() == reflect.Float64)fmt.Println("value:", v.Float())