Golang面向API編程-interface(接口)

                    Golang面向API編程-interface(接口)

                                          作者:尹正杰

版權聲明:原創作品,謝絕轉載!否則將追究法律責任。

?

 

?

  Golang并不是一種典型的面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)語言。它在語法上不支持類和繼承的概念。沒有繼承是否就無法擁有多態行為了呢?答案是否定的,我們知道 Golang 中沒有 class 的概念,而是通過 interface 類型轉換支持在動態類型語言中常見的“鴨子類型”達到運行時多態的效果。

?

一.什么是interface

  簡單的說,interface是一組method的組合,我們通過interface來定義對象的一組行為。換句話說,一個 interface 類型定義了一個“方法集合”作為其接口。?interface類型的變量可以保存含有屬于這個interface類型的任何類型的值,這時我們就說這個類型實現了這個接口。未被初始化的interface類型變量的零值為空(nil)。

?

二.interface類型和值

  接口類型實際上是一組method(方法)簽名的清單。interface 類型定義了一組方法,如果某個對象實現了某個接口的所有方法,則此對象就實現了此接口。接口也是一種數據類型。如果你聲明了一個接口變量,這個變量能夠存儲任何實現該接口的對象類型。最后,任意的類型都實現了空interface(我們這樣定義:interface{}),也就是包含 0 個method的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     "strings"
13 )
14 
15 type Human struct {
16     Name string            //定義姓名
17     string                //內置的匿名字段,我們用它來定義家庭住址。
18     phone int            //電話號碼
19 }
20 
21 type Student struct {
22     Human                 //匿名字段,其類型就是我們上面自定義的類型。
23     Classroom string    //教室名稱
24     Score float64        //考試成績
25 }
26 
27 type Teacher struct {
28     Human                 //匿名字段
29     Position string        //職位信息
30     salary float64        //薪資情況
31 }
32 
33 
34 func (h Human) SayHi() {                    //定義結構體“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) {          //定義結構體“Human”唱歌的方法
41     fmt.Printf("【%s】:我有一只小毛驢我從來也不騎,有一天我心血來潮騎它去趕集.....\n", Name)
42 }
43 
44 
45 func (t Teacher) SayHi() {                    //定義結構體“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 {                    //定義一個接口
52     SayHi()                                    //這個接口包含“SayHi()    ”方法。
53     Sing(Name string)                        //該接口也包含“Sing(Name string)”方法。
54 }
55 
56 func main() {
57     yzj := Student{Human{"尹正杰", "北京", 7474741}, "三年級一班", 95} //初始化我們定義的結構體。我們也可以將這個過程叫做實例化。而將“yzj”叫做實例。
58     hsy := Student{Human{"韓森雨", "北京", 2424241}, "一年級五班", 100}
59     bingan := Teacher{Human{"餅干", "北京", 6464641}, "Golang", 30000}
60 
61 
62     var yinzhengjie Superman  //聲明一個變量,其類型為我們定義的接口。
63 
64     yinzhengjie = yzj                        //注意了,這是接受了我們定義第一個類型。
65     yinzhengjie.SayHi()                        //并且可以調用這個類型下的“SayHi()”方法已經“Sing("高音唱")”方法。
66     yinzhengjie.Sing("高音唱")
67 
68 
69     fmt.Println("\n",strings.Repeat("*",30),"我是分割線",strings.Repeat("*",30),"\n")
70 
71     yin := make([]Superman,3)    //處理上面的那種最普遍的玩法,當然也可以用make方法將其定義成切片的方式。
72     yin[0], yin[1], yin[2] = hsy, bingan, yzj //用下標來區分不同的實力。這個時候我們可以發現Superman類型可以接受“Student”和“Teacher”類型的數據,盡管這是兩個不同的結構體,但是照樣可以通過一個接口來調用,因此我叫它“大胃王”。
73 
74     for _, value := range yin{ //然后我們就可以同時調用3個方法啦!
75         value.SayHi()
76     }
77 }
78 
79 
80 
81 #以上代碼執行結果如下:
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 可以被任意的對象實現。同理,一個對象可以實現任意多個interface。你會發現interface 就是一組抽象方法的集合,它必須由其他非interface類型實現,而不能自我實現, go 通過 interface 實現了duck-typing:即"當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子"。

?

三.空interface

?  空interface(interface{})不包含任何的 method,正因為如此,所有的類型都實現了空interface。空 interface 對于描述起不到任何的作用(因為它不包含任何的 method),但是空interface 在我們需要存儲任意類型的數值的時候相當有用,因為它可以存儲任意類型的數值。一個函數把interface{}作為參數,那么他可以接受任意類型的值作為參數,如果一個函數返回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 := "尹正杰"                      //我這里定義了三種不同的類型,即字符串,整數,字節數等等。
21       Age := 18
22       Language := []byte("Golang")
23       fmt.Println(reflect.TypeOf(Name),reflect.TypeOf(Age),reflect.TypeOf(Language))
24       var yinzhengjie interface{}            //定義一個空的interface,由于每種數據類型都實現了空interface。因此我們利用這個特性可以接受任意類型的數據。
25       yinzhengjie = Name
26       Myecho(yinzhengjie)
27       yinzhengjie = Age
28       Myecho(yinzhengjie)
29       yinzhengjie = Language
30       Myecho(yinzhengjie)
31 }
32 
33 
34 
35 #以上代碼輸出結果如下:
36 string int []uint8
37 變量的值是:【尹正杰】,其類型是:【string】
38 變量的值是:【18】,其類型是:【int】
39 變量的值是:【[71 111 108 97 110 103]】,其類型是:【[]uint8】

?

?

四.interface 函數參數

  ?interface 的變量可以持有任意實現該 interface 類型的對象,這給我們編寫函數(包括method)提供了一些額外的思考,我們是不是可以通過定義 interface 參數,讓函數接受各種類型的參數。舉個例子:fmt.Println 是我們常用的一個函數,但是你是否注意到它可以接受任意類型的數據。打開fmt的源碼文件,你會看到這樣一個定義:

?

  接下來我們就來模擬實現“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實現來String方法,如果我們把String前面加個其他字母或是進行其他修改,可能會導致該方法的內容不會被調用。
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 #以上代碼輸出結果如下:
33 This people is : My name is 尹正杰,I am 18 years old.I live in 北京

?

五.interface 變量存儲的類型

  我們知道interface 的變量里面可以存儲任意類型的數值(該類型實現了interface)。那么我們怎么反向知道這個變量里面實際保存了的是哪個類型的對象呢?目前常用的有兩種方法:“Comma-ok 斷言”?和“switch 測試”。

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   (    //這是定義一個常量的關鍵字
16     pi = 3.14
17 )
18 
19 type Student struct {
20     Name string
21     age int
22 }
23 
24 //定義了 String 方法,實現了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                                               // 定義一個“int”類型的數據。
31     list[1] = "Hello"                                         // 定義一個“string”類型的數據。
32     list[2] = Student{"Yinzhengjie", 18}    // 定義一個“Student”類型的數據。
33     list[3] = pi                                            //定義一個常量。
34 
35     for index, element := range list {      //接下來就是判斷里面的每一個元素屬于哪一種類型。
36         if value, ok := element.(int); ok {        //判斷當前的數據類型是否為“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 {    //判斷當前的數據類型是否為“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 {    //判斷當前的數據類型是否為自定義的“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 #以上代碼輸出結果如下:
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 測試

  如果上面的那種方式你能看懂,并且之前我也分享過golang流程控制的筆記,那么下面的這種斷言方式對你來說就是小case啦~從代碼的易讀性的話我推薦使用這種方式進行對數據類型的斷言。

 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 外面判斷一個類型就使用 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 #以上代碼輸出結果如下:
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里面真正吸引人的是他內置的邏輯語法,就像我們在學習Struct 時學習的匿名字段,多么的優雅啊,那么相同的邏輯引入到 interface 里面,那不是更加完美了。如果一個interface1 作為 interface2 的一個嵌入字段,那么 interface2 隱式的包含了interface1 里面的method。接下來我們就來舉個例子自定義實現以下嵌入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 {    // 定義結構 Employee
13     Name   string
14     age    int
15     salary int
16     gender string
17 }
18 
19 // 定義結構 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和嵌入匿名字段的用法有點相似。
47     GetSalary() int
48     Help()
49 }
50 
51 func main() {
52     yzj := Student{        // yzj 實現了 MiyoshiStudents 和 Teacher 這兩個接口
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) {    // 接口類型轉換,從超集到子集的轉換是可以的,從方法集的子集到超集的轉換會導致編譯錯誤,這種情況下 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 #以上代碼執行結果如下:
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 接口

?

七.匿名接口

  還記得匿名字段嗎?我們可以在一個結構體中定義一個匿名字段,這個匿名字段可以是內置的也可以是我們自定義的,而interface是一種特殊的數據類型,因為golang認為所有的數據類型都實現了空接口,也就是說所有數據都是空interface的子集。換句話說,我們可以說一個空的interface是可以接受任何類型的數據的。通過這一點,它會給我們很多啟發嗎,我們可以通過interface來接受任何類型的參數,也可以通過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 "fmt"
11 
12 
13 type Student struct {    // 定義結構體Student
14     Name   string
15     age    int
16     salary int
17     gender string
18 }
19 
20 func (self *Student) GetName() string {        // 定義結構 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 {    // 匿名接口可以被用作變量或者結構屬性類型,我們定義了一個“GetInfo”的匿名接口,里面可以存儲各種數據屬性。
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{        // 匿名接口對象的使用
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 #以上代碼運行結果如下:
67 姓名: 尹正杰
68 年齡: 18
69 性別:  男孩
70 期望薪資: 10000000000

?

八.進階知識-Go語言的反射三定律

1.什么是反射

  反射是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力,所以給的定義就是說明了它能干嘛。我們平時用反射主要做:獲取類型的相關信息,動態調用方法,動態構造對象,從程序集中獲得類型。

?

2.為什么需要反射

  Go是靜態類型語言。每個變量都有且只有一個靜態類型,在編譯時就已經確定。盡管變量兩個邊路都具有共同的底層數據類型,但它們的只要他們靜態類型不一樣。不經過類型轉換直接相互賦值時,編譯器會報錯。相信大家通過一段熟悉的代碼就應該明白了:

 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{}    //定義一個空接口
15 
16 var (
17     x int
18     y Myint  //盡管變量 x 和 y 具有共同的底層類型 int,但它們的靜態類型并不一樣。
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) {        //我們隊數據類型進行斷言。
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 #以上代碼執行結果如下:
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.反射第一定律:從接口值到反射對象的反射(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 簽名里就包含了一個空接口。當我們調用reflect.Typeof(yzj)的時候,
18     // yzj首先被保存到一個空接口中,這個空接口然后被作為參數傳遞。reflect.Typeof 會把這個空接口拆包(unpack)恢復出類型信息。
19 
20     fmt.Println("value:", reflect.ValueOf(yzj))       //當然,reflect.Valueof可以把值恢復出來,Valueof方法會返回一個Value類型的對象
21 }
22 
23 
24 
25 #以上代碼執行結果如下:
26 type: float64
27 value: 5.2

  reflect.Type和reflect.Value這兩種類型都提供了大量的方法讓我們可以檢查和操作這兩種類型。有以下兩點要注意:

  第一,Value類型有一個Type方法可以返回reflect.Value類型的Type,這個方法返回的是值的靜態類型即“static type”,也就是說如果定義了“type MyType string”,那么這個函數返回的是“MyType”類型而不是“string”。有關Value類型的帶有名字諸如“String”,“Int”,“Uint”“Bytes”等等的方法可讓我們獲取存在里面的值。

  第二,Type和Value都有一個Kind方法可以返回一個常量用于指示一個項到底是以什么形式存儲的,也就是底層類型即“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) //得到類型的元數據,通過 t 我們能獲取類型定義里面的所有元素.
20     Value := reflect.ValueOf(y)    //得到實際的值,通過 v 我們獲取存儲在里面的值,還可以去改變值.
21     fmt.Println("type\t\t\t:",Type)
22     fmt.Println("underlying type    :",Type.Kind())    //Type和Value都有一個Kind方法可以返回一個常量,以判斷出它的底層數據到底是什么類型。
23     fmt.Println("value\t\t\t:",Value)
24     fmt.Println("static type        :",Value.Type())    //Value類型有一個Type方法可以返回reflect.Value類型的Type(這個方法返回的是值的靜態類型即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方法來讓我們獲取存在里面的值。如果是底層數據是“int”就用“Int”方法獲取。
28 }
29 
30 
31 
32 
33 #以上代碼執行結果如下:
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.反射第二定律:從反射對象到接口值的反射(Reflection goes from reflection object to interface value)

?  就像物理學上的作用力和反作用力,我們可以從接口值到反射對象,與此同時,我們也可以從反射對象到接口值。

  給定一個reflect.Value,我們能用Interface方法把它恢復成一個接口值;效果上就是這個Interface方法把類型和值的信息打包成一個接口表示并且返回結果。簡要的說,Interface方法是Valueof函數的逆,除了它的返回值的類型總是interface{}靜態類型。重申一遍:反射就是從接口值到反射對象,然后再反射回來。(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)    //得到實際的值,通過 v 我們獲取存儲在里面的值,還可以去改變值.
21     fmt.Println(Value)            //Value是一個reflect.Value.
22     
23     x := Value.Interface()        //我們想要的是Value里面保存的具體值.我們不需要對v.Interface方法的結果調用類型斷言
24     fmt.Println(x)
25 }
26 
27 
28 
29 #以上代碼執行結果如下:
30 yinzhengjie
31 yinzhengjie

?

5.反射第三定律:為了修改一個反射對象,值必須是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地址傳進去了!
18     fmt.Println("type of p:", p.Type())                //我們是講地址傳進去的,所以得到的應該是一個指針類型的string.
19     fmt.Println("settability of p:", p.CanSet())        //反射對象p不是settable的,因此返回值應該是一個false!
20 
21     v := p.Elem()                                            //反射對象p不是settable的,但是我們想要設置的不是p,而是(效果上來說)*p,為了得到p指向的東西,我們調用Value的Elem方法。
22 
23     fmt.Println(v.Interface()) //查看v里面的值
24     s := v.String()
25     s = "尹正杰"        //我們此處修改的只是“yzj”變量中的一個副本
26     fmt.Println(s)
27     fmt.Println(yzj)    //憂郁s修改的是副本,所以對本尊是一點影響的都沒有的,源數據應該還是“yinzhengjie”
28 
29 
30     fmt.Println("settability of v:", v.CanSet())        //反射對象v是settable的,因此返回值應該是一個true!
31     v.SetString("Golang")    //想要修改源數據,還是得調用該SetString,SetInt,SetFloat,等方法去修改相應的數據類型。
32     fmt.Println(yzj)    //由于已經通過SetString方法對源數據進行了修改,因此我們再看yzj這個變量的值就已經被修改了。
33 }
34 
35 
36 
37 
38 #以上代碼執行結果如下:
39 type of p: *string
40 settability of p: false
41 yinzhengjie
42 尹正杰
43 yinzhengjie
44 settability of v: true
45 Golang

?

?

?

?

?

?

?

轉載于:https://www.cnblogs.com/yinzhengjie/p/7733420.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280029.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280029.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280029.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux學習_菜鳥教程_3

我是在UBANTO上運行Linux的,開機啟動時按下shift或者Esc都不能進入到grub,沒有百度到可靠的教程。 暫時先這樣吧。免得我把系統搞壞了,先學點實用的知識~~ Next Chapter轉載于:https://www.cnblogs.com/sggggr/p/9233627.html

如何使用 EF Core 7 批量刪除數據

在 EF Core 7 中,我們可以使用批量操作來刪除多條數據。這種方式與之前的版本有所不同,本文將對比 EFCore 7 和之前版本批量刪除數據的不同方式。刪除給定 ID 的數據 在 EF Core 7 中,我們可以使用以下代碼來刪除給定 ID 的數據:a…

筆記本禁用鍵盤命令符_如何在Windows中禁用命令提示符和“運行”程序

筆記本禁用鍵盤命令符The Command Prompt and the Run program are pretty powerful tools in the Windows world. If you’d rather specific users on a computer not have access to them, it’s not too hard to do. 命令提示符和運行程序是Windows世界中非常強大的工具。 …

MySQL Date 函數

2019獨角獸企業重金招聘Python工程師標準>>> MySQL 中最重要的內建日期函數: NOW() 返回當前的日期和時間 CURDATE() 返回當前的日期 CURTIME() 返回當前的時間 DATE() 提取日期或日期/時間表達式的日期部分 EXTRACT() 返回日期/時間按的…

Android之Window與WindowManager

Window表示一個窗口的概念,在日常開發中直接接觸Window的機會并不多,但卻會經常用到Window,activity、toast、dialog、PopupWindow、狀態欄等都是Window。在Android中Window是個抽象類,并且僅有一個實現類PhoneWindow。 1、Window…

C# WPF This用法詳解(經典)

概述this在C#中有多種用法,也比較常見,這節主要針對它常用的四種用法展開講解.用法1:構造函數串聯執行;用法2:通過this區分傳參和類中全局的定義;用法3:方法擴展類;用法4:將對象作為參數傳遞;代碼實例using System.Text;namespace Caliburn.Micro.Hello.…

前端node 和vue開發之環境搭建

下載nvm nodejs 的快捷鍵是配置后自動生成的 nvm 的 setting.txt配置 root: C:\dev\nvmpath: C:\dev\nodejsarch: 32proxy: root指向 nvm.exeroot: C:\dev\nvmpath: C:\dev\nodejs 配置環境變量 變量名 變量值 GIT_HOME C:\dev…

如何從特定位置開始分享YouTube視頻

Tech tutorials that start with 3 minutes of “hey guys what’s up” are the worst. Get to the point! Here’s how you can bypass that nonsense when sharing a video with your friends. 最糟糕的是從3分鐘的“嗨,大家好起來”開始的技術教程。 講到重點&a…

解決git提交問題error: The requested URL returned error: 403 Forbidden while accessing

2019獨角獸企業重金招聘Python工程師標準>>> git提交代碼時,出現這個錯誤“error: The requested URL returned error: 403 Forbidden while accessing https” 解決方法: 編輯.git目錄下的config文件即可。 vim .git/config [core] …

基于.NetCore開發博客項目 StarBlog - (24) 統一接口數據返回格式

1前言開發接口,是給客戶端(Web前端、App)用的,前面說的RESTFul,是接口的規范,有了統一的接口風格,客戶端開發人員在訪問后端功能的時候能更快找到需要的接口,能寫出可維護性更高的代…

如何將C# 7類庫升級到C# 8?使用可空引用類型

這篇文章將介紹將C# 7類庫升級到C# 8(支持可空引用類型)的一個案例。本案例中使用的項目Tortuga Anchor由一組MVVM風格的基類、反射代碼和各種實用程序函數組成。之所以選擇這個項目,是因為它很小,并且同時包含了慣用和不常用的C#…

android 設備名稱_如何更改您的Android TV的設備名稱

android 設備名稱Android TV is Google’s attempt at taking over the living room, and with some units being available for under $99, it’s not unheard of for users to have more than one box. The problem is, when multiple devices identify themselves identical…

AD-查找符合指定條件的用戶Get-User

以下服務器為Exchange 2010一、使用 Get-User 命令查找部門為IT的用戶Get-User -ResultSize Unlimited | ? { $_.Department -Eq "IT" } | ft Name,Department二、查找注釋為多行內容的指定用戶如下圖:注釋Notes信息為多行要使用 match 和 (?*) 來做匹配…

目標檢測算法之Fast R-CNN算法詳解

在介紹Fast R-CNN之前我們先介紹一下SPP Net 一、SPP Net SPP:Spatial Pyramid Pooling(空間金字塔池化) 眾所周知,CNN一般都含有卷積部分和全連接部分,其中,卷積層不需要固定尺寸的圖像,而全連…

RGB-D(深度圖像) 圖像深度

RGB-D(深度圖像) 深度圖像 普通的RGB三通道彩色圖像 Depth Map 在3D計算機圖形中,Depth Map(深度圖)是包含與視點的場景對象的表面的距離有關的信息的圖像或圖像通道。其中,Depth Map 類似于灰度圖像&…

WPF-21 基于MVVM員工管理-01

接下來我們通過兩節課程使用MVVM來開發一個簡單的Demo,首先我們創建一個項目名稱WPF-22-MVVM-Demo,目錄結構如下:我們在Models文件下創建Employee類并讓該類實現INotifyPropertyChanged接口,該類中定義編號、姓名和角色三個基本屬…

qt 蘋果應用程序_什么是蘋果的電視應用程序,您應該使用它嗎?

qt 蘋果應用程序Apple’s TV app, which recently appeared on iOS devices and Apple TV, is meant to help users discover and watch shows across an increasingly expanding lineup of television channels, as well as iTunes movies and shows, in one central app. App…

細說flush、ob_flush的區別

ob_flush/flush在手冊中的描述, 都是刷新輸出緩沖區, 并且還需要配套使用, 所以會導致很多人迷惑… 其實, 他們倆的操作對象不同, 有些情況下, flush根本不做什么事情.. ob_*系列函數, 是操作PHP本身的輸出緩沖區. 所以, ob_flush是刷新PHP自身的緩沖區. 而flush, 嚴格來講, 這…

關于jHipster框架在構建中的出現的error修復

jhipster The JDL object and the database type are both mandatory.這個錯誤應該是在構建基于jHipster的spring-cloud項目中經常遇到的,因為這個在這個過程中會讀取.yo-rc文件,之后生成相關的.json文件,再之后生成相關的.java文件&#xff…

protobuf編碼

proto2Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用于結構化數據序列化,適合做數據存儲或 RPC 數據交換格式。可用于通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。 字段規則 required: 字段必須存在opti…