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

Golang面向API编程-interface(接口)相关推荐

  1. api接口怎么写_面向声明式API编程(DAP)

    DAP是Mars-java 最近提出的一个新的开发方式,全称 Declarative API Programming, 提倡后端为一个独立的整体,不应该是为前端服务的,所以当前端需要接口的时候,只需要 ...

  2. 面向切面编程实现Nestjs接口Api数据缓存

    一.业务场景 在后端接口开发过程中,我们经常会谈论的话题,提高接口响应速度,前端接口调用后端接口响应时间的缩短,我们抛开数据库设计及后端代码的业务逻辑等问题.我们经常会听到说用redis做数据缓存,直 ...

  3. Java 设计模式 Day2 之面向抽象原则:接口(interface)的设计应用与抽象类的区别

    文章目录 前言 一.接口(interface)的设计要点与抽象类的区别 二.接口(interface)的应用实例 2.1.创建一个接口(interface) 2.2.创建实现接口的类 2.3.通过接口 ...

  4. Golang interface 接口详细原理和使用技巧

    文章目录 Golang interface 接口详细原理和使用技巧 一.Go interface 介绍 interface 在 Go 中的重要性说明 interface 的特性 interface 接 ...

  5. 面向“接口”编程和面向“实现”编程

    英文原文:Program to an Interface, Fool 如果你已经读了我的前几篇关于面向对象范式因为受到 Rust, Go 等语言的影响而发生变化的文章,看到了我正在研究的 Rust 设 ...

  6. AOP面向切面编程之全局日志打印/统计接口耗时

    目录 一.什么是AOP 二.AOP使用场景 三.使用AOP的好处 四.先举个例子理解AOP面向切面编程 五.Spring5.X的AOP切入点表达式有这些种写法 六.实战基于Spring的AOP快速实现 ...

  7. Java 面向抽象编程和面向接口编程

    以下内容来自<Java 2实用教程>,主编:耿祥义.张跃平 鉴于面向抽象编程和面向接口编程思维培养的重要性,写此博客巩固. 面向抽象编程: 在设计程序时,经常会使用到abstract类,其 ...

  8. Spring→面向切面编程AOP、相关概念、通知Advice类型、配置切面切入点通知、AOP相关API、AOP代理类ProxyFactoryBean、AOP注解@AspectJ

    面向切面编程AOP CGLib AOP相关概念 Advice类型 Spring实现AOP Spring配置切面aspect 配置切入点pointcut 配置通知advice 配置通知参数 调用新的父类 ...

  9. golang TCP Socket编程

    Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分.在日常应用中,我们也可以看到Go中的net以及其subdirectories下的包均 ...

最新文章

  1. 资源|深度学习注意力机制TensorFlow 使用教程
  2. 删除数组中指定元素_如何删除PHP数组元素键值并重新排序
  3. 两个场景怎样合在一起_Spring AOP应用场景你还不知道?这篇一定要看
  4. python 使用小知识总结(持续更新ing)
  5. mysql的命令行常用命令_mysql命令行常用命令
  6. vue.js框架搭建
  7. 研究生阅读管理文献---我阅读科研文献的一些做法
  8. 使用Python进行描述性统计
  9. javascript高级程序设计第二章知识点提炼
  10. 原生 javascript 操作 websocket
  11. 基因学:大数据的大希望
  12. JavaWbe学习总结之jQuery
  13. 现代数字图像处理作业----对lena.bmp图像采用高频强调滤波增强方法,并分析方法的效果。(理想、巴特沃斯、高斯) 其结果好不好?能否有改善的方法?
  14. -XX:SurvivorRatio 命令解释
  15. [POI2012]SZA-Cloakroom
  16. IT大学生成长周报 | 第 4 期
  17. [Chatter] 错误处理的安全保证等级
  18. 分享一个聊天机器人接口API,很好用,智能,而且免费。
  19. RK G87机械键盘使用指南
  20. 《明日歌》《今日诗》《昨日谣》

热门文章

  1. 大数据平台助力核与辐射安全监管
  2. HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别(转)
  3. Akka(9): 分布式运算:Remoting-远程构建式
  4. memcached应用策略(转)
  5. jps: command not found
  6. 计算机应用基础126题,2015-2016年全国计算机应用基础总复习.
  7. linux 安装nginx php mysql 配置文件在哪_linux下 php+nginx+mysql安装配置
  8. Ubuntu磁盘扩容及启动问题整理
  9. 全国计算机等级考试题库二级C操作题100套(第54套)
  10. java某个类避免findbug检查_Findbugs能否在java中检测到捕获RuntimeException?