<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 2.6 interface ## interface Go語言里面設計最精妙的應該算interface,它讓面向對象,內容組織實現非常的方便,當你看完這一章,你就會被interface的巧妙設計所折服。 ### 什么是interface 簡單的說,interface是一組method的組合,我們通過interface來定義對象的一組行為。 我們前面一章最后一個例子中Student和Employee都能SayHi,雖然他們的內部實現不一樣,但是那不重要,重要的是他們都能`say hi` 讓我們來繼續做更多的擴展,Student和Employee實現另一個方法`Sing`,然后Student實現方法BorrowMoney而Employee實現SpendSalary。 這樣Student實現了三個方法:SayHi、Sing、BorrowMoney;而Employee實現了SayHi、Sing、SpendSalary。 上面這些方法的組合稱為interface(被對象Student和Employee實現)。例如Student和Employee都實現了interface:SayHi和Sing,也就是這兩個對象是該interface類型。而Employee沒有實現這個interface:SayHi、Sing和BorrowMoney,因為Employee沒有實現BorrowMoney這個方法。 ### interface類型 interface類型定義了一組方法,如果某個對象實現了某個接口的所有方法,則此對象就實現了此接口。詳細的語法參考下面這個例子 type Human struct { name string age int phone string } type Student struct { Human //匿名字段Human school string loan float32 } type Employee struct { 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! } // 定義interface type Men interface { SayHi() Sing(lyrics string) Guzzle(beerStein string) } type YoungChap interface { SayHi() Sing(song string) BorrowMoney(amount float32) } type ElderlyGent interface { SayHi() Sing(song string) SpendSalary(amount float32) } 通過上面的代碼我們可以知道,interface可以被任意的對象實現。我們看到上面的Men interface被Human、Student和Employee實現。同理,一個對象可以實現任意多個interface,例如上面的Student實現了Men和YoungChap兩個interface。 最后,任意的類型都實現了空interface(我們這樣定義:interface{}),也就是包含0個method的interface。 ### interface值 那么interface里面到底能存什么值呢?如果我們定義了一個interface的變量,那么這個變量里面可以存實現這個interface的任意類型的對象。例如上面例子中,我們定義了一個Men interface類型的變量m,那么m里面可以存Human、Student或者Employee值。 因為m能夠持有這三種類型的對象,所以我們可以定義一個包含Men類型元素的slice,這個slice可以被賦予實現了Men接口的任意結構的對象,這個和我們傳統意義上面的slice有所不同。 讓我們來看一下下面這個例子: package main import "fmt" type Human struct { name string age int phone string } type Student struct { Human //匿名字段 school string loan float32 } type Employee struct { 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實現 // 因為這三個類型都實現了這兩個方法 type Men interface { SayHi() Sing(lyrics string) } func main() { 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類型的變量i var 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, mike for _, value := range x{ value.SayHi() } } 通過上面的代碼,你會發現interface就是一組抽象方法的集合,它必須由其他非interface類型實現,而不能自我實現, Go通過interface實現了duck-typing:即"當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子"。 ### 空interface 空interface(interface{})不包含任何的method,正因為如此,所有的類型都實現了空interface。空interface對于描述起不到任何的作用(因為它不包含任何的method),但是空interface在我們需要存儲任意類型的數值的時候相當有用,因為它可以存儲任意類型的數值。它有點類似于C語言的void*類型。 // 定義a為空接口 var a interface{} var i int = 5 s := "Hello world" // a可以存儲任意類型的數值 a = i a = s 一個函數把interface{}作為參數,那么他可以接受任意類型的值作為參數,如果一個函數返回interface{},那么也就可以返回任意類型的值。是不是很有用啊! ### interface函數參數 interface的變量可以持有任意實現該interface類型的對象,這給我們編寫函數(包括method)提供了一些額外的思考,我們是不是可以通過定義interface參數,讓函數接受各種類型的參數。 舉個例子:fmt.Println是我們常用的一個函數,但是你是否注意到它可以接受任意類型的數據。打開fmt的源碼文件,你會看到這樣一個定義: type Stringer interface { String() string } 也就是說,任何實現了String方法的類型都能作為參數被fmt.Println調用,讓我們來試一試 package main import ( "fmt" "strconv" ) type Human struct { name string age int phone string } // 通過這個方法 Human 實現了 fmt.Stringer func (h Human) String() string { return "?"+h.name+" - "+strconv.Itoa(h.age)+" years - ? " +h.phone+"?" } func main() { Bob := Human{"Bob", 39, "000-7777-XXX"} fmt.Println("This Human is : ", Bob) } 現在我們再回顧一下前面的Box示例,你會發現Color結構也定義了一個method:String。其實這也是實現了fmt.Stringer這個interface,即如果需要某個類型能被fmt包以特殊的格式輸出,你就必須實現Stringer這個接口。如果沒有實現這個接口,fmt將以默認的方式輸出。 //實現同樣的功能 fmt.Println("The biggest one is", boxes.BiggestsColor().String()) fmt.Println("The biggest one is", boxes.BiggestsColor()) 注:實現了error接口的對象(即實現了Error() string的對象),使用fmt輸出時,會調用Error()方法,因此不必再定義String()方法了。 ### interface變量存儲的類型 我們知道interface的變量里面可以存儲任意類型的數值(該類型實現了interface)。那么我們怎么反向知道這個變量里面實際保存了的是哪個類型的對象呢?目前常用的有兩種方法: - Comma-ok斷言 Go語言里面有一個語法,可以直接判斷是否是該類型的變量: value, ok = element.(T),這里value就是變量的值,ok是一個bool類型,element是interface變量,T是斷言的類型。 如果element里面確實存儲了T類型的數值,那么ok返回true,否則返回false。 讓我們通過一個例子來更加深入的理解。 package main import ( "fmt" "strconv" ) type Element interface{} type List [] Element type Person struct { name string age int } //定義了String方法,實現了fmt.Stringer func (p Person) String() string { return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { 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) } else if value, ok := element.(string); ok { fmt.Printf("list[%d] is a string and its value is %s\n", index, value) } else if 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測試 最好的講解就是代碼例子,現在讓我們重寫上面的這個實現 package main import ( "fmt" "strconv" ) type Element interface{} type List [] Element type Person struct { name string age int } //打印 func (p Person) String() string { return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { 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) { case int: fmt.Printf("list[%d] is an int and its value is %d\n", index, value) case string: fmt.Printf("list[%d] is a string and its value is %s\n", index, value) case Person: 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) } } } 這里有一點需要強調的是:`element.(type)`語法不能在switch外的任何邏輯里面使用,如果你要在switch外面判斷一個類型就使用`comma-ok`。 ### 嵌入interface Go里面真正吸引人的是它內置的邏輯語法,就像我們在學習Struct時學習的匿名字段,多么的優雅啊,那么相同的邏輯引入到interface里面,那不是更加完美了。如果一個interface1作為interface2的一個嵌入字段,那么interface2隱式的包含了interface1里面的method。 我們可以看到源碼包container/heap里面有這樣的一個定義 type Interface interface { sort.Interface //嵌入字段sort.Interface Push(x interface{}) //a Push method to push elements into the heap Pop() interface{} //a Pop elements that pops elements from the heap } 我們看到sort.Interface其實就是嵌入字段,把sort.Interface的所有method給隱式的包含進來了。也就是下面三個方法: type Interface interface { // 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) } 另一個例子就是io包下面的 io.ReadWriter ,它包含了io包下面的Reader和Writer兩個interface: // io.ReadWriter type ReadWriter interface { Reader Writer } ### 反射 Go語言實現了反射,所謂反射就是能檢查程序在運行時的狀態。我們一般用到的包是reflect包。如何運用reflect包,官方的這篇文章詳細的講解了reflect包的實現原理,[laws of reflection](http://golang.org/doc/articles/laws_of_reflection.html) 使用reflect一般分成三步,下面簡要的講解一下:要去反射是一個類型的值(這些值都實現了空interface),首先需要把它轉化成reflect對象(reflect.Type或者reflect.Value,根據不同的情況調用不同的函數)。這兩種獲取方式如下: t := reflect.TypeOf(i) //得到類型的元數據,通過t我們能獲取類型定義里面的所有元素 v := reflect.ValueOf(i) //得到實際的值,通過v我們獲取存儲在里面的值,還可以去改變值 轉化為reflect對象之后我們就可以進行一些操作了,也就是將reflect對象轉化成相應的值,例如 tag := t.Elem().Field(0).Tag //獲取定義在struct里面的標簽 name := v.Elem().Field(0).String() //獲取存儲在第一個字段里面的值 獲取反射值能返回相應的類型和數值 var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float()) 最后,反射的話,那么反射的字段必須是可修改的,我們前面學習過傳值和傳引用,這個里面也是一樣的道理。反射的字段必須是可讀寫的意思是,如果下面這樣寫,那么會發生錯誤 var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) 如果要修改相應的值,必須這樣寫 var x float64 = 3.4 p := reflect.ValueOf(&x) v := p.Elem() v.SetFloat(7.1) 上面只是對反射的簡單介紹,更深入的理解還需要自己在編程中不斷的實踐。 ## links * [目錄](<preface.md>) * 上一章: [面向對象](<02.5.md>) * 下一節: [并發](<02.7.md>)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看