Golang面向API编程-interface(接口)
Golang面向API編程-interface(接口)
作者:尹正杰
版權(quán)聲明:原創(chuàng)作品,謝絕轉(zhuǎn)載!否則將追究法律責(zé)任。
?
?
Golang并不是一種典型的面向?qū)ο缶幊?#xff08;Object Oriented Programming,OOP,面向?qū)ο蟪绦蛟O(shè)計(jì))語言。它在語法上不支持類和繼承的概念。沒有繼承是否就無法擁有多態(tài)行為了呢?答案是否定的,我們知道 Golang 中沒有 class 的概念,而是通過 interface 類型轉(zhuǎn)換支持在動(dòng)態(tài)類型語言中常見的“鴨子類型”達(dá)到運(yùn)行時(shí)多態(tài)的效果。
?
一.什么是interface
簡(jiǎn)單的說,interface是一組method的組合,我們通過interface來定義對(duì)象的一組行為。換句話說,一個(gè) interface 類型定義了一個(gè)“方法集合”作為其接口。?interface類型的變量可以保存含有屬于這個(gè)interface類型的任何類型的值,這時(shí)我們就說這個(gè)類型實(shí)現(xiàn)了這個(gè)接口。未被初始化的interface類型變量的零值為空(nil)。
?
二.interface類型和值
接口類型實(shí)際上是一組method(方法)簽名的清單。interface 類型定義了一組方法,如果某個(gè)對(duì)象實(shí)現(xiàn)了某個(gè)接口的所有方法,則此對(duì)象就實(shí)現(xiàn)了此接口。接口也是一種數(shù)據(jù)類型。如果你聲明了一個(gè)接口變量,這個(gè)變量能夠存儲(chǔ)任何實(shí)現(xiàn)該接口的對(duì)象類型。最后,任意的類型都實(shí)現(xiàn)了空interface(我們這樣定義:interface{}),也就是包含 0 個(gè)method的interface。所以我喜歡給它起一個(gè)綽號(hào)叫“大胃王”。
定義一個(gè)interface以及調(diào)用方式如下:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "fmt" 12 "strings" 13 ) 14 15 type Human struct { 16 Name string //定義姓名 17 string //內(nèi)置的匿名字段,我們用它來定義家庭住址。 18 phone int //電話號(hào)碼 19 } 20 21 type Student struct { 22 Human //匿名字段,其類型就是我們上面自定義的類型。 23 Classroom string //教室名稱 24 Score float64 //考試成績(jī) 25 } 26 27 type Teacher struct { 28 Human //匿名字段 29 Position string //職位信息 30 salary float64 //薪資情況 31 } 32 33 34 func (h Human) SayHi() { //定義結(jié)構(gòu)體“Human”自我介紹的方法。 35 fmt.Printf("Hello, my name is 【%s】. My phone number is【%d】.My home address is【%s】\n", h.Name, 36 h.phone,h.string) //格式化輸出是可以換行的喲。 37 } 38 39 40 func (h Human) Sing(Name string) { //定義結(jié)構(gòu)體“Human”唱歌的方法 41 fmt.Printf("【%s】:我有一只小毛驢我從來也不騎,有一天我心血來潮騎它去趕集.....\n", Name) 42 } 43 44 45 func (t Teacher) SayHi() { //定義結(jié)構(gòu)體“Teacher”自我介紹的方法。 46 fmt.Printf("Anyway, I'm your 【%s】 teacher, you can call me 【%s】,My salary is...【%f】\n", t.Position, 47 t.Human.Name,t.salary) 48 } 49 50 51 type Superman interface { //定義一個(gè)接口 52 SayHi() //這個(gè)接口包含“SayHi() ”方法。 53 Sing(Name string) //該接口也包含“Sing(Name string)”方法。 54 } 55 56 func main() { 57 yzj := Student{Human{"尹正杰", "北京", 7474741}, "三年級(jí)一班", 95} //初始化我們定義的結(jié)構(gòu)體。我們也可以將這個(gè)過程叫做實(shí)例化。而將“yzj”叫做實(shí)例。 58 hsy := Student{Human{"韓森雨", "北京", 2424241}, "一年級(jí)五班", 100} 59 bingan := Teacher{Human{"餅干", "北京", 6464641}, "Golang", 30000} 60 61 62 var yinzhengjie Superman //聲明一個(gè)變量,其類型為我們定義的接口。 63 64 yinzhengjie = yzj //注意了,這是接受了我們定義第一個(gè)類型。 65 yinzhengjie.SayHi() //并且可以調(diào)用這個(gè)類型下的“SayHi()”方法已經(jīng)“Sing("高音唱")”方法。 66 yinzhengjie.Sing("高音唱") 67 68 69 fmt.Println("\n",strings.Repeat("*",30),"我是分割線",strings.Repeat("*",30),"\n") 70 71 yin := make([]Superman,3) //處理上面的那種最普遍的玩法,當(dāng)然也可以用make方法將其定義成切片的方式。 72 yin[0], yin[1], yin[2] = hsy, bingan, yzj //用下標(biāo)來區(qū)分不同的實(shí)力。這個(gè)時(shí)候我們可以發(fā)現(xiàn)Superman類型可以接受“Student”和“Teacher”類型的數(shù)據(jù),盡管這是兩個(gè)不同的結(jié)構(gòu)體,但是照樣可以通過一個(gè)接口來調(diào)用,因此我叫它“大胃王”。 73 74 for _, value := range yin{ //然后我們就可以同時(shí)調(diào)用3個(gè)方法啦! 75 value.SayHi() 76 } 77 } 78 79 80 81 #以上代碼執(zhí)行結(jié)果如下: 82 Hello, my name is 【尹正杰】. My phone number is【7474741】.My home address is【北京】 83 【高音唱】:我有一只小毛驢我從來也不騎,有一天我心血來潮騎它去趕集..... 84 85 ****************************** 我是分割線 ****************************** 86 87 Hello, my name is 【韓森雨】. My phone number is【2424241】.My home address is【北京】 88 Anyway, I'm your 【Golang】 teacher, you can call me 【餅干】,My salary is...【30000.000000】 89 Hello, my name is 【尹正杰】. My phone number is【7474741】.My home address is【北京】? 通過上面的代碼我們可以知道,interface 可以被任意的對(duì)象實(shí)現(xiàn)。同理,一個(gè)對(duì)象可以實(shí)現(xiàn)任意多個(gè)interface。你會(huì)發(fā)現(xiàn)interface 就是一組抽象方法的集合,它必須由其他非interface類型實(shí)現(xiàn),而不能自我實(shí)現(xiàn), go 通過 interface 實(shí)現(xiàn)了duck-typing:即"當(dāng)看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子"。
?
三.空interface
? 空interface(interface{})不包含任何的 method,正因?yàn)槿绱?#xff0c;所有的類型都實(shí)現(xiàn)了空interface。空 interface 對(duì)于描述起不到任何的作用(因?yàn)樗话魏蔚?method),但是空interface 在我們需要存儲(chǔ)任意類型的數(shù)值的時(shí)候相當(dāng)有用,因?yàn)樗梢源鎯?chǔ)任意類型的數(shù)值。一個(gè)函數(shù)把interface{}作為參數(shù),那么他可以接受任意類型的值作為參數(shù),如果一個(gè)函數(shù)返回interface{},那么也就可以返回任意類型的值。是不是瞬間就覺得interface很神奇!
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "fmt" 12 "reflect" 13 ) 14 15 func Myecho(a interface{}) { 16 fmt.Printf("變量的值是:\033[31;1m【%v】\033[0m,其類型是:\033[31;1m【%v】\033[0m\n",a,reflect.TypeOf(a)) 17 } 18 19 func main() { 20 Name := "尹正杰" //我這里定義了三種不同的類型,即字符串,整數(shù),字節(jié)數(shù)等等。 21 Age := 18 22 Language := []byte("Golang") 23 fmt.Println(reflect.TypeOf(Name),reflect.TypeOf(Age),reflect.TypeOf(Language)) 24 var yinzhengjie interface{} //定義一個(gè)空的interface,由于每種數(shù)據(jù)類型都實(shí)現(xiàn)了空interface。因此我們利用這個(gè)特性可以接受任意類型的數(shù)據(jù)。 25 yinzhengjie = Name 26 Myecho(yinzhengjie) 27 yinzhengjie = Age 28 Myecho(yinzhengjie) 29 yinzhengjie = Language 30 Myecho(yinzhengjie) 31 } 32 33 34 35 #以上代碼輸出結(jié)果如下: 36 string int []uint8 37 變量的值是:【尹正杰】,其類型是:【string】 38 變量的值是:【18】,其類型是:【int】 39 變量的值是:【[71 111 108 97 110 103]】,其類型是:【[]uint8】?
?
四.interface 函數(shù)參數(shù)
?interface 的變量可以持有任意實(shí)現(xiàn)該 interface 類型的對(duì)象,這給我們編寫函數(shù)(包括method)提供了一些額外的思考,我們是不是可以通過定義 interface 參數(shù),讓函數(shù)接受各種類型的參數(shù)。舉個(gè)例子:fmt.Println 是我們常用的一個(gè)函數(shù),但是你是否注意到它可以接受任意類型的數(shù)據(jù)。打開fmt的源碼文件,你會(huì)看到這樣一個(gè)定義:
?
接下來我們就來模擬實(shí)現(xiàn)“fmt.Println()”的stringer方法吧:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "strconv" 12 "fmt" 13 ) 14 15 type Student struct { 16 Name string //定義姓名 17 age int //定義年齡 18 string //定義住址,這是匿名字段 19 } 20 21 func (s Student)String()string { //給Student實(shí)現(xiàn)來String方法,如果我們把String前面加個(gè)其他字母或是進(jìn)行其他修改,可能會(huì)導(dǎo)致該方法的內(nèi)容不會(huì)被調(diào)用。 22 return "My name is "+ s.Name+",I am "+strconv.Itoa(s.age)+" years old.I live in "+s.string 23 } 24 25 func main() { 26 yzj := Student{"尹正杰",18,"北京"} 27 fmt.Println("This people is :",yzj) 28 } 29 30 31 32 #以上代碼輸出結(jié)果如下: 33 This people is : My name is 尹正杰,I am 18 years old.I live in 北京?
五.interface 變量存儲(chǔ)的類型
我們知道interface 的變量里面可以存儲(chǔ)任意類型的數(shù)值(該類型實(shí)現(xiàn)了interface)。那么我們?cè)趺捶聪蛑肋@個(gè)變量里面實(shí)際保存了的是哪個(gè)類型的對(duì)象呢?目前常用的有兩種方法:“Comma-ok 斷言”?和“switch 測(cè)試”。
1.Comma-ok 斷言
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 import ( 10 "fmt" 11 "strconv" 12 ) 13 type Element interface{} 14 15 const ( //這是定義一個(gè)常量的關(guān)鍵字 16 pi = 3.14 17 ) 18 19 type Student struct { 20 Name string 21 age int 22 } 23 24 //定義了 String 方法,實(shí)現(xiàn)了fmt.Stringer 25 func (p Student) String() string { 26 return "(name: " + p.Name + " - age: "+strconv.Itoa(p.age)+ " years old!)" 27 } 28 func main() { 29 list := make([]Element, 4) 30 list[0] = 1 // 定義一個(gè)“int”類型的數(shù)據(jù)。 31 list[1] = "Hello" // 定義一個(gè)“string”類型的數(shù)據(jù)。 32 list[2] = Student{"Yinzhengjie", 18} // 定義一個(gè)“Student”類型的數(shù)據(jù)。 33 list[3] = pi //定義一個(gè)常量。 34 35 for index, element := range list { //接下來就是判斷里面的每一個(gè)元素屬于哪一種類型。 36 if value, ok := element.(int); ok { //判斷當(dāng)前的數(shù)據(jù)類型是否為“int”類型 37 fmt.Printf("list[%d] is an int and its value is %d\n", index, value) 38 } else if value, ok := element.(string); ok { //判斷當(dāng)前的數(shù)據(jù)類型是否為“string”類型 39 fmt.Printf("list[%d] is a string and its value is %s\n", index, value) 40 } else if value, ok := element.(Student); ok { //判斷當(dāng)前的數(shù)據(jù)類型是否為自定義的“Student”類型 41 fmt.Printf("list[%d] is a Student and its value is %s\n", index, value) 42 } else { 43 fmt.Printf("list[%d] is of a different type!", index) 44 } 45 } 46 } 47 48 49 50 51 #以上代碼輸出結(jié)果如下: 52 list[0] is an int and its value is 1 53 list[1] is a string and its value is Hello 54 list[2] is a Student and its value is (name: Yinzhengjie - age: 18 years old!) 55 list[3] is of a different type!?
2.switch 測(cè)試
如果上面的那種方式你能看懂,并且之前我也分享過golang流程控制的筆記,那么下面的這種斷言方式對(duì)你來說就是小case啦~從代碼的易讀性的話我推薦使用這種方式進(jìn)行對(duì)數(shù)據(jù)類型的斷言。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 9 package main 10 11 import ( 12 "strconv" 13 "fmt" 14 ) 15 16 type Element interface {} 17 18 19 type Student struct { 20 Name string 21 age int 22 } 23 24 func (p Student) String() string { 25 return "My name is "+ p.Name+" and I am "+ strconv.Itoa(p.age) +" years old!" 26 } 27 28 func main() { 29 list := make([]Element,3) 30 list[0] = 1 31 list[1] = "yinzhengjie" 32 list[2] = Student{"yinzhengjie",18} 33 34 for k,v := range list { 35 switch value := v.(type) { //element.(type) 語法不能在switch 外的任何邏輯里面使用,如果你要在switch 外面判斷一個(gè)類型就使用 comma-ok 。 36 case int: 37 fmt.Printf("list[%d] is an int and its value is %d\n",k,value) 38 case string: 39 fmt.Printf("list[%d] is an string and its value is %s\n",k,value) 40 case Student: 41 fmt.Printf("list[%d] is an Student and its value is %v\n",k,value) 42 default: 43 fmt.Printf("list[%d] is of a different\n",) 44 } 45 } 46 } 47 48 49 50 51 #以上代碼輸出結(jié)果如下: 52 list[0] is an int and its value is 1 53 list[1] is an string and its value is yinzhengjie 54 list[2] is an Student and its value is My name is yinzhengjie and I am 18 years old!?
?
六.嵌入 interface
看到官方使用的嵌入interface方法你是否想到我們之前說的匿名字段啦?Go里面真正吸引人的是他內(nèi)置的邏輯語法,就像我們?cè)趯W(xué)習(xí)Struct 時(shí)學(xué)習(xí)的匿名字段,多么的優(yōu)雅啊,那么相同的邏輯引入到 interface 里面,那不是更加完美了。如果一個(gè)interface1 作為 interface2 的一個(gè)嵌入字段,那么 interface2 隱式的包含了interface1 里面的method。接下來我們就來舉個(gè)例子自定義實(shí)現(xiàn)以下嵌入interface的案例吧,具體代碼如下:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Student struct { // 定義結(jié)構(gòu) Employee 13 Name string 14 age int 15 salary int 16 gender string 17 } 18 19 // 定義結(jié)構(gòu) Employee 的方法 20 func (self *Student) GetName() string { 21 return self.Name 22 } 23 24 func (self *Student) GetAge() int { 25 return self.age 26 } 27 28 func (self *Student) GetSalary() int { 29 return self.salary 30 } 31 32 func (self *Student) Help() { 33 fmt.Println("Don't ask me, ask me, I won't tell you!") 34 } 35 36 func (self *Student) GetGender() string { 37 return self.gender 38 } 39 40 type MiyoshiStudents interface { // 定義接口類型 MiyoshiStudents 包含獲取基本信息的方法 41 GetName() string 42 GetAge() int 43 } 44 45 type Teacher interface { // 定義接口類型 Teacher 包含獲取薪水的方法且 Teacher 接口中嵌入了 MiyoshiStudents 接口,前者將獲取后者的所有方法。 46 MiyoshiStudents //這就是嵌入interface和嵌入匿名字段的用法有點(diǎn)相似。 47 GetSalary() int 48 Help() 49 } 50 51 func main() { 52 yzj := Student{ // yzj 實(shí)現(xiàn)了 MiyoshiStudents 和 Teacher 這兩個(gè)接口 53 Name: "尹正杰", 54 age: 18, 55 salary: 100000000, 56 gender: "Male", 57 } 58 fmt.Println("yzj is: ", yzj) 59 yzj.Help() 60 fmt.Println("yzj.name = ", yzj.GetName()) 61 fmt.Println("yzj.age = ", yzj.GetAge()) 62 fmt.Println("yzj.salary = ", yzj.GetSalary()) 63 64 var yinzhengjie Teacher = &yzj 65 66 switch yinzhengjie.(type) { // 接口類型轉(zhuǎn)換,從超集到子集的轉(zhuǎn)換是可以的,從方法集的子集到超集的轉(zhuǎn)換會(huì)導(dǎo)致編譯錯(cuò)誤,這種情況下 switch 不支持 fallthrough。 67 case nil: 68 fmt.Println("空接口(nil)") 69 case MiyoshiStudents: 70 fmt.Println("MiyoshiStudents 接口") 71 default: 72 fmt.Println("位置接口") 73 } 74 } 75 76 77 78 79 #以上代碼執(zhí)行結(jié)果如下: 80 yzj is: {尹正杰 18 100000000 Male} 81 Don't ask me, ask me, I won't tell you! 82 yzj.name = 尹正杰 83 yzj.age = 18 84 yzj.salary = 100000000 85 MiyoshiStudents 接口?
七.匿名接口
還記得匿名字段嗎?我們可以在一個(gè)結(jié)構(gòu)體中定義一個(gè)匿名字段,這個(gè)匿名字段可以是內(nèi)置的也可以是我們自定義的,而interface是一種特殊的數(shù)據(jù)類型,因?yàn)間olang認(rèn)為所有的數(shù)據(jù)類型都實(shí)現(xiàn)了空接口,也就是說所有數(shù)據(jù)都是空interface的子集。換句話說,我們可以說一個(gè)空的interface是可以接受任何類型的數(shù)據(jù)的。通過這一點(diǎn),它會(huì)給我們很多啟發(fā)嗎,我們可以通過interface來接受任何類型的參數(shù),也可以通過interface來返回任何類型的參數(shù),接下來我們一起看下匿名interface的使用案例吧:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 13 type Student struct { // 定義結(jié)構(gòu)體Student 14 Name string 15 age int 16 salary int 17 gender string 18 } 19 20 func (self *Student) GetName() string { // 定義結(jié)構(gòu) Student 的方法 21 return self.Name 22 } 23 24 func (self *Student) GetAge() int { 25 return self.age 26 } 27 28 func (self *Student) GetSalary() int { 29 return self.salary 30 } 31 32 func (self *Student) Help() { 33 fmt.Println("This is help info.") 34 } 35 36 type MiyoshiStudents struct { 37 GetInfo interface { // 匿名接口可以被用作變量或者結(jié)構(gòu)屬性類型,我們定義了一個(gè)“GetInfo”的匿名接口,里面可以存儲(chǔ)各種數(shù)據(jù)屬性。 38 GetGender() string 39 GetSalary() int 40 GetAge() int 41 GetName() string 42 } 43 } 44 45 func (self *Student) GetGender() string { 46 return self.gender 47 } 48 49 50 func main() { 51 yzj := MiyoshiStudents{&Student{ // 匿名接口對(duì)象的使用 52 Name: "尹正杰", 53 age: 18, 54 salary: 10000000000, 55 gender: "男孩", 56 }} 57 fmt.Println("姓名:",yzj.GetInfo.GetName()) 58 fmt.Println("年齡:",yzj.GetInfo.GetAge()) 59 fmt.Println("性別: ", yzj.GetInfo.GetGender()) 60 fmt.Println("期望薪資:",yzj.GetInfo.GetSalary()) 61 62 } 63 64 65 66 #以上代碼運(yùn)行結(jié)果如下: 67 姓名: 尹正杰 68 年齡: 18 69 性別: 男孩 70 期望薪資: 10000000000?
八.進(jìn)階知識(shí)-Go語言的反射三定律
1.什么是反射
反射是指程序可以訪問、檢測(cè)和修改它本身狀態(tài)或行為的一種能力,所以給的定義就是說明了它能干嘛。我們平時(shí)用反射主要做:獲取類型的相關(guān)信息,動(dòng)態(tài)調(diào)用方法,動(dòng)態(tài)構(gòu)造對(duì)象,從程序集中獲得類型。
?
2.為什么需要反射
Go是靜態(tài)類型語言。每個(gè)變量都有且只有一個(gè)靜態(tài)類型,在編譯時(shí)就已經(jīng)確定。盡管變量?jī)蓚€(gè)邊路都具有共同的底層數(shù)據(jù)類型,但它們的只要他們靜態(tài)類型不一樣。不經(jīng)過類型轉(zhuǎn)換直接相互賦值時(shí),編譯器會(huì)報(bào)錯(cuò)。相信大家通過一段熟悉的代碼就應(yīng)該明白了:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Myint int 13 14 type Element interface{} //定義一個(gè)空接口 15 16 var ( 17 x int 18 y Myint //盡管變量 x 和 y 具有共同的底層類型 int,但它們的靜態(tài)類型并不一樣。 19 ) 20 func main() { 21 x = 100 22 y = 100 23 list := make([]Element, 2) 24 list[0] = x 25 list[1] = y 26 fmt.Println(list) 27 for k,v := range list{ 28 switch value := v.(type) { //我們隊(duì)數(shù)據(jù)類型進(jìn)行斷言。 29 case int: 30 fmt.Printf("list[%d] is an int(整型) and its value is %d\n",k,value) 31 case string: 32 fmt.Printf("list[%d] is an string(字符串) and its value is %d\n",k,value) 33 case Myint: 34 fmt.Printf("list[%d] is an Myint(自定義類型) and its value is %d\n",k,value) 35 default: 36 fmt.Printf("list[%d] is of a different\n",) 37 } 38 } 39 } 40 41 42 43 44 #以上代碼執(zhí)行結(jié)果如下: 45 [100 100] 46 list[0] is an int(整型) and its value is 100 47 list[1] is an Myint(自定義類型) and its value is 100?
3.反射第一定律:從接口值到反射對(duì)象的反射(Reflection goes from interface value to reflection object)
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "fmt" 12 "reflect" 13 ) 14 15 func main() { 16 var yzj float64 = 5.2 17 fmt.Println("type:", reflect.TypeOf(yzj)) //reflect.Typeof 簽名里就包含了一個(gè)空接口。當(dāng)我們調(diào)用reflect.Typeof(yzj)的時(shí)候, 18 // yzj首先被保存到一個(gè)空接口中,這個(gè)空接口然后被作為參數(shù)傳遞。reflect.Typeof 會(huì)把這個(gè)空接口拆包(unpack)恢復(fù)出類型信息。 19 20 fmt.Println("value:", reflect.ValueOf(yzj)) //當(dāng)然,reflect.Valueof可以把值恢復(fù)出來,Valueof方法會(huì)返回一個(gè)Value類型的對(duì)象 21 } 22 23 24 25 #以上代碼執(zhí)行結(jié)果如下: 26 type: float64 27 value: 5.2reflect.Type和reflect.Value這兩種類型都提供了大量的方法讓我們可以檢查和操作這兩種類型。有以下兩點(diǎn)要注意:
第一,Value類型有一個(gè)Type方法可以返回reflect.Value類型的Type,這個(gè)方法返回的是值的靜態(tài)類型即“static type”,也就是說如果定義了“type MyType string”,那么這個(gè)函數(shù)返回的是“MyType”類型而不是“string”。有關(guān)Value類型的帶有名字諸如“String”,“Int”,“Uint”“Bytes”等等的方法可讓我們獲取存在里面的值。
第二,Type和Value都有一個(gè)Kind方法可以返回一個(gè)常量用于指示一個(gè)項(xiàng)到底是以什么形式存儲(chǔ)的,也就是底層類型即“underlying type”。這些常量包括:Unit, Float64, Slice等等。
具體用法我們可以以下的代碼:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "reflect" 12 "fmt" 13 ) 14 15 type MyType string 16 17 func main() { 18 var y MyType = "yinzhengjie" 19 Type := reflect.TypeOf(y) //得到類型的元數(shù)據(jù),通過 t 我們能獲取類型定義里面的所有元素. 20 Value := reflect.ValueOf(y) //得到實(shí)際的值,通過 v 我們獲取存儲(chǔ)在里面的值,還可以去改變值. 21 fmt.Println("type\t\t\t:",Type) 22 fmt.Println("underlying type :",Type.Kind()) //Type和Value都有一個(gè)Kind方法可以返回一個(gè)常量,以判斷出它的底層數(shù)據(jù)到底是什么類型。 23 fmt.Println("value\t\t\t:",Value) 24 fmt.Println("static type :",Value.Type()) //Value類型有一個(gè)Type方法可以返回reflect.Value類型的Type(這個(gè)方法返回的是值的靜態(tài)類型即static type.) 25 fmt.Println("underlying type :",Value.Kind()) 26 fmt.Println("kind is string :",Value.Kind() == reflect.String) 27 fmt.Println("value\t\t\t:",Value.String()) //通過Value類型String方法來讓我們獲取存在里面的值。如果是底層數(shù)據(jù)是“int”就用“Int”方法獲取。 28 } 29 30 31 32 33 #以上代碼執(zhí)行結(jié)果如下: 34 type : main.MyType 35 underlying type : string 36 value : yinzhengjie 37 static type : main.MyType 38 underlying type : string 39 kind is string : true 40 value : yinzhengjie?
4.反射第二定律:從反射對(duì)象到接口值的反射(Reflection goes from reflection object to interface value)
? 就像物理學(xué)上的作用力和反作用力,我們可以從接口值到反射對(duì)象,與此同時(shí),我們也可以從反射對(duì)象到接口值。
給定一個(gè)reflect.Value,我們能用Interface方法把它恢復(fù)成一個(gè)接口值;效果上就是這個(gè)Interface方法把類型和值的信息打包成一個(gè)接口表示并且返回結(jié)果。簡(jiǎn)要的說,Interface方法是Valueof函數(shù)的逆,除了它的返回值的類型總是interface{}靜態(tài)類型。重申一遍:反射就是從接口值到反射對(duì)象,然后再反射回來。(Reflection goes from interface value to reflection object and back again.)
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "reflect" 12 "fmt" 13 ) 14 15 type MyType string 16 17 18 func main() { 19 var y MyType = "yinzhengjie" 20 Value := reflect.ValueOf(y) //得到實(shí)際的值,通過 v 我們獲取存儲(chǔ)在里面的值,還可以去改變值. 21 fmt.Println(Value) //Value是一個(gè)reflect.Value. 22 23 x := Value.Interface() //我們想要的是Value里面保存的具體值.我們不需要對(duì)v.Interface方法的結(jié)果調(diào)用類型斷言 24 fmt.Println(x) 25 } 26 27 28 29 #以上代碼執(zhí)行結(jié)果如下: 30 yinzhengjie 31 yinzhengjie?
5.反射第三定律:為了修改一個(gè)反射對(duì)象,值必須是settable的(To modify a reflection object, the value must be settable)
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "reflect" 12 "fmt" 13 ) 14 15 func main() { 16 var yzj string = "yinzhengjie" 17 p := reflect.ValueOf(&yzj) //注意這里哦!我們把yzj地址傳進(jìn)去了! 18 fmt.Println("type of p:", p.Type()) //我們是講地址傳進(jìn)去的,所以得到的應(yīng)該是一個(gè)指針類型的string. 19 fmt.Println("settability of p:", p.CanSet()) //反射對(duì)象p不是settable的,因此返回值應(yīng)該是一個(gè)false! 20 21 v := p.Elem() //反射對(duì)象p不是settable的,但是我們想要設(shè)置的不是p,而是(效果上來說)*p,為了得到p指向的東西,我們調(diào)用Value的Elem方法。 22 23 fmt.Println(v.Interface()) //查看v里面的值 24 s := v.String() 25 s = "尹正杰" //我們此處修改的只是“yzj”變量中的一個(gè)副本 26 fmt.Println(s) 27 fmt.Println(yzj) //憂郁s修改的是副本,所以對(duì)本尊是一點(diǎn)影響的都沒有的,源數(shù)據(jù)應(yīng)該還是“yinzhengjie” 28 29 30 fmt.Println("settability of v:", v.CanSet()) //反射對(duì)象v是settable的,因此返回值應(yīng)該是一個(gè)true! 31 v.SetString("Golang") //想要修改源數(shù)據(jù),還是得調(diào)用該SetString,SetInt,SetFloat,等方法去修改相應(yīng)的數(shù)據(jù)類型。 32 fmt.Println(yzj) //由于已經(jīng)通過SetString方法對(duì)源數(shù)據(jù)進(jìn)行了修改,因此我們?cè)倏磞zj這個(gè)變量的值就已經(jīng)被修改了。 33 } 34 35 36 37 38 #以上代碼執(zhí)行結(jié)果如下: 39 type of p: *string 40 settability of p: false 41 yinzhengjie 42 尹正杰 43 yinzhengjie 44 settability of v: true 45 Golang?
?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/yinzhengjie/p/7733420.html
總結(jié)
以上是生活随笔為你收集整理的Golang面向API编程-interface(接口)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows10电脑重置后右键没有nv
- 下一篇: 2021-11-16