這是一個建立於
的文章,其中的資訊可能已經有所發展或是發生改變。
There are three kinds of time: the future is late, and now it is flying like an arrow. The past will never stand still.
時間的步伐有三種:未來姍姍來遲,現在像箭一般飛逝,過去永遠靜立不動。
什麼是interface
在物件導向編程中,可以這麼說:“介面定義了對象的行為”, 那麼具體的實現行為就取決於對象了。
在Go中,介面是一組方法簽名。當一個類型為介面中的所有方法提供定義時,它被稱為實現該介面。它與oop非常相似。介面指定類型應具有的方法,類型決定如何?這些方法。
建立和實現interface
在Golang中只要實現了介面定義的方法,就是(JAVA implement)實現了該interface
package mainimport ( "fmt")//定義interface type VowelsFinder interface { FindVowels() []rune}type MyString string//實現介面func (ms MyString) FindVowels() []rune { var vowels []rune for _, rune := range ms { if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' { vowels = append(vowels, rune) } } return vowels}func main() { name := MyString("Sam Anderson") // 類型轉換 var v VowelsFinder // 定義一個介面類型的變數 v = name fmt.Printf("Vowels are %c", v.FindVowels())}
介面的實際用途
為什麼說是實際用途呢? 如果我們我上面code中的
fmt.Printf("Vowels are %c", v.FindVowels())
替換為
fmt.Printf("Vowels are %c", name.FindVowels())
程式同樣的輸出,而沒有使用我們定義的介面。(v變數刪除定義)
下面我們通過案例解釋:
假設某公司有兩個員工,一個普通員工和一個進階員工, 但是基本薪資是相同的,進階員工多拿獎金。計算公司為員工的總開支。
package mainimport ("fmt")// 薪資計算機介面type SalaryCalculator interface {CalculateSalary() int}// 普通挖掘機員工type Contract struct {empId intbasicpay int}// 有藍翔技校證的員工type Permanent struct {empId intbasicpay intjj int // 獎金}func (p Permanent) CalculateSalary() int {return p.basicpay + p.jj}func (c Contract) CalculateSalary() int {return c.basicpay}// 總開支func totalExpense(s []SalaryCalculator) {expense := 0for _, v := range s {expense = expense + v.CalculateSalary()}fmt.Printf("總開支 $%d", expense)}func main() {pemp1 := Permanent{1,3000,10000}pemp2 := Permanent{2, 3000, 20000}cemp1 := Contract{3, 3000}employees := []SalaryCalculator{pemp1, pemp2, cemp1}totalExpense(employees)}
體驗一下使用介面的美感吧!
介面的內部表現
一個介面可以被認為是由一個元組(類型,值)在內部表示的。type是介面的基礎具體類型,value是具體類型的值。
package mainimport ("fmt")type Test interface {Tester()}type MyFloat float64func (m MyFloat) Tester() {fmt.Println(m)}func describe(t Test) {fmt.Printf("Interface 類型 %T , 值: %v\n", t, t)}func main() {var t Testf := MyFloat(89.7)t = fdescribe(t)t.Tester()}
輸出:
Interface 類型 main.MyFloat , 值: 89.7
89.7
空介面
具有0個方法的介面稱為空白介面。它表示為interface {}。由於空介面有0個方法,所有類型都實現了空介面。
package mainimport ("fmt")func describe(i interface{}) {fmt.Printf("Type = %T, value = %v\n", i, i)}func main() { // 任何類型的變數傳入都可以s := "Hello World"i := 55strt := struct {name string}{name: "Naveen R",}describe(s)describe(i)describe(strt)}
類型斷言
類型斷言用於提取介面的基礎值,文法:i.(T)
package mainimport("fmt")func assert(i interface{}){ s:= i.(int) fmt.Println(s)}func main(){ var s interface{} = 55 assert(s)}
程式列印的是int值, 但是如果我們給s 變數賦值的是string類型,程式就會panic。
所以可以將以上程式改寫為:
package mainimport ( "fmt")func assert(i interface{}) { v, ok := i.(int) fmt.Println(v, ok)}func main() { var s interface{} = 56 assert(s) var i interface{} = "Steven Paul" assert(i)}
如果 i 的值是int類型, 那麼v就是i 對應的值, ok就是true。否則ok為false,程式並不會panic。
類型判斷
類型判斷的文法類似於類型斷言。在類型斷言的文法i.(type)中,類型type應該由類型轉換的關鍵字type替換。讓我們看看它如何在下面的程式中起作用。
package mainimport ( "fmt")func findType(i interface{}) { switch i.(type) { case string: fmt.Printf("String: %s\n", i.(string)) case int: fmt.Printf("Int: %d\n", i.(int)) default: fmt.Printf("Unknown type\n") }}func main() { findType("Naveen") findType(77) findType(89.98)}
還可以將類型與介面進行比較。如果我們有一個類型並且該類型實現了一個介面,那麼可以將它與它實現的介面進行比較。
package mainimport "fmt"type Describer interface { Describe()}type Person struct { name string age int}func (p Person) Describe() { fmt.Printf("%s is %d years old", p.name, p.age)}func findType(i interface{}) { switch v := i.(type) { case Describer: v.Describe() default: fmt.Printf("unknown type\n") }}func main() { findType("Naveen") p := Person{ name: "Naveen R", age: 25, } findType(p)}
輸出:
unknown type Naveen R is 25 years old
最後,留一個小問題,猜想一下,下面程式的輸出結果:
package mainimport "fmt"type Describer interface { Describe()}type St stringfunc (s St) Describe() { fmt.Println("被調用le!")}func findType(i interface{}) { switch v := i.(type) { case Describer: v.Describe() case string: fmt.Println("String 變數") default: fmt.Printf("unknown type\n") }}func main() { findType("Naveen") st := St("我的字串") findType(p)}