Go语言开荒中空interface

上面这段代码是以数据以空接口的形式返回,但是空interface在我们需要存储任意类型的数值的时候相当有用,这时我们就说这个类型实现了这个接口, interface类型的变量可以保存含有属于这个interface类型的任何类型的值,这些参数的类型全部是int,通过new函数分配一个指针,然后Student实现方法BorrowMoney而Employee实现SpendSalary,Student和Employee实现另一个方法Sing

图片 1

res2.(*StructIPToAddress)正是空中接力口转换来StructIPToAddress的写法

  第一,Value类型有一个Type方法能够再次回到reflect.Value类型的Type,那个主意重临的是值的静态类型即“static
type”,也等于说假设定义了“type MyType
string”,那么这一个函数再次回到的是“MyType”类型并不是“string”。有关Value类型的蕴含名字诸如“String”,“Int”,“Uint”“Bytes”等等的措施可让大家收获存在内部的值。

与此相类似就能够在友好的代码里面定义有含义的品类了,实际上只是一个定义了二个小名,有一点类似于c中的typedef,举例地点ages代替了int

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     "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 北京

func main() {
    list := make(List, 3)
    list[0] = 1 // an int
    list[1] = “Hello” // a string
    list[2] = Person{“Dennis”, 70}

做iOS开辟的时候大家驾驭id指的是自由数据类型的靶子,那么在Go语言开荒有未有如此的数据类型,答案是确定的,这种数据类型便是空interface,空interface(interface{})不分包其余的method,正因为如此,全部的项目都落到实处了空interface。空interface对于描述起不到另外的法力(因为它不含有其余的method),不过空interface在我们须求仓库储存放肆档案的次序的数值的时候一定有用,因为它可以积攒猖狂档期的顺序的数值。它有一点点类似于C语言的void*体系,OC中的id类型,然而oc中的id只可以是引用类型,空interface既能为引用类型也足以为值类型。

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

new重临指针
例:通过new函数分配一个指针,此处P的门类为*person

    //i能存储Student
    i = mike
    fmt.Println(“This is Mike, a Student:”)
    i.SayHi()
    i.Sing(“November rain”)

在go语言的时候,当在多个艺术的时候,不一致尺度重回分裂的等级次序的数量,那时候用上空中接力口就很方便了,举个例子上边这段代码

  即便上边的这种情势你能看懂,何况以前笔者也享受过golang流程序调整制的笔记,那么下面包车型大巴这种断言格局对您来讲便是小case啦~从代码的易读性的话作者引入应用这种措施展开对数据类型的断言。

  • Go的switch非常灵活,表达式不必是常量或整数,实施的经过从上至下,直到找到相称项;而只要switch未有表明式,它会相配true。

package main

// 定义a为空接口var a interface{}var i int = 5s := "Hello world"// a可以存储任意类型的数值a = ia = s

  具体用法我们得以以下的代码:

make用于内建品种(map、slice
和channel)的内部存款和储蓄器分配。new用于各个类型的内存分配。
内建函数new本质上说跟任何语言中的同名函数成效雷同:new(T)分配了零值填充的T类型的内部存款和储蓄器空间,並且再次回到其地方,即三个*T类型的值。用Go的术语说,它回到了二个指南针,指向新分配的类型T的零值。有好几要命重大:

//Human对象实现Guzzle方法
func (h *Human) Guzzle(beerStein string) {
    fmt.Println(“Guzzle Guzzle Guzzle…”, beerStein)
}

// StructIPToAddress IP 到 地址type StructIPToAddress struct { Address string `json:"address"` Content struct { Address string `json:"address"` AddressDetail struct { City string `json:"city"` CityCode int64 `json:"city_code"` District string `json:"district"` Province string `json:"province"` Street string `json:"street"` StreetNumber string `json:"street_number"` Point struct { X string `json:"x"` Y string `json:"y"` } `json:"point"` } `json:"address_detail"` } `json:"content"` Status int64 `json:"status"` Message string `json:"message"`}// requestBaidu 构建 HTTP 请求func requestBaidu(reqType, reqURL string) (interface{}, error) { res, err := getResStruct if err != nil { return res, err } httpClient := http.Client{} resp, err := httpClient.Get if err != nil { return res, err } defer resp.Body.Close() bytes, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode == 200 { err := json.Unmarshal(bytes, &res) if err != nil { return res, err } } else { return res, errors.New("请求百度API失败,状态码不等于200") } return res, nil}// getResStruct 处理百度 API 返回数据,映射到结构体中func getResStruct(reqType string) (interface{}, error) { var res interface{} if reqType == "GetAddressViaIP" { return new(StructIPToAddress), nil } if reqType == "GetGeoViaAddress" { return new(StructAddressToGEO), nil } if reqType == "GetAddressViaGEO" { return new(StructGEOToAddress), nil } return res, errors.New("结构体请求错误")}
 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!
  • 大写字母开端的变量是可导出的,也正是任何包能够读取的,是公有变量;小写字母起初的正是不足导出的,是私人商品房变量。
  • 大写字母开首的函数也是平等,相当于class中的带public关键词的公有函数;小写字母初始的正是有private关键词的个人函数。
  • map的初叶化可以通过key:val的法子初叶化值,同不时间map内置有判别是还是不是留存key的办法,通过delete删除map的成分:

另一个例证正是io包上面包车型客车 io.ReadWriter
,它含有了io包上面包车型客车Reader和Writer多个interface:

// GetAddressViaIP 通过 IP 获取地址func GetAddressViaIP(address string) (*StructIPToAddress, error) { res := new(StructIPToAddress) parameter := fmt.Sprintf("&ip=%s&output=json&pois=0", address) reqURL := fmt.Sprintf("%s%s%s", reqURLForIP, AppKey, parameter) res2, err := requestBaidu("GetAddressViaIP", reqURL) if err != nil { return res, err } if res2.(*StructIPToAddress).Status != 0 { message := fmt.Sprintf("百度 API 报错:%s", res2.(*StructIPToAddress).Message) return res, errors.New } res3 := res2.(*StructIPToAddress) return res3, nil}
 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

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
}

参照文书档案

查看interface介绍

  Golang并不是一种标准的面向对象编制程序(Object
Oriented
Programming,OOP,面向对象程序设计)语言。它在语法上不协理类和三番五次的定义。未有持续是或不是就无法具备多态行为了呢?答案是还是不是定的,大家明白Golang 中未有 class 的定义,而是经过 interface
类型转变辅助在动态类型语言中常见的“鸭子类型”到达运转时多态的成效。

type Stringer interface {
     String() string
}

复制代码 代码如下:

 

由此地点的代码我们能够领略,interface能够被随意的靶子完结。我们看见地点的Men
interface被Human、Student和Employee达成。同理,叁个对象能够达成自由多个interface,举例地方的Student实现了Men和YoungChap三个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 接口
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()

func main() {
    list := make(List, 3)
    list[0] = 1 //an int
    list[1] = “Hello” //a string
    list[2] = Person{“Dennis”, 70}

2.switch 测试

  • log

func main() {
    Bob := Human{“Bob”, 39, “000-7777-XXX”}
    fmt.Println(“This Human is : “, Bob)
}

5.反射第三定律:为了修改三个反光对象,值必需是settable的(Tomodify a reflection object, the value must be settable)

if v, ok := varI.(T); ok {  // checked type assertion
    Process(v)
    return
}
// varI is not of type T

type YoungChap interface {
    SayHi()
    Sing(song string)
    BorrowMoney(amount float32)
}

 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

也正是说,任何实现了String方法的连串(也正是完成了Stringer接口)都能看做参数被fmt.Println调用,让我们来试一试

嵌入interface

一.什么是interface

classifier(13, -14.3, “BELGIUM”, complex(1, 2), nil, false) 。

// 定义a为空中接力口
var a interface{}
var i int = 5
s := “Hello world”
// a能够积累任意等级次序的数值
a = i
a = s

 

sum := 1
for sum < 1000 {
    sum += sum
}

type Employee struct {
    Human //无名字段
    company string
    money float32
}

图片 1

func myfunc(arg …int) {}

Comma-ok断言

 

有五个系列 stockPosition 和 car,它们都有一个 getValue()
方法,大家能够定义三个具有此措施的接口 valuable。接着定义三个运用
valuable 类型作为参数的函数 showValue(),具备完结了
valuable接口的连串都能够用这几个函数

// 定义interface
type Men interface {
    SayHi()
    Sing(lyrics string)
    Guzzle(beerStein string)
}

   就如物医学上的效用力和反功本领,大家得以从接口值到反射对象,与此同一时间,大家也足以从反射对象到接口值。

  • defer

要是要修改相应的值,必需这样写

 

*interface函数参数
(要是急需有个别项目能被fmt包以新鲜的格式输出,你就亟须兑现Stringer那一个接口。若无兑现那个接口,fmt将以私下认可的点子出口)

tag := t.Elem().Field(0).Tag  //获取定义在struct里面包车型地铁价签
name := v.Elem().Field(0).String()  //获取存储在第一个字段里面包车型地铁值

4.反射次之定律:从反射对象到接口值的反射(Reflection
goes from reflection object to interface value)

package main
import “fmt”

四.interface 函数参数

package main

import (
    "fmt"
)

func main() {
    sss := fmt.Sprintf("fdsfds%s", "sdfdsfsd")
    fmt.Println(sss)
}

>>> fdsfdssdfdsfsd

type Student struct {
    Human //无名字段
    school string
    loan float32
}

八.进级知识-Go语言的反射三定律

请看下边这么些发明自定义类型的代码

//打印
func (p Person) String() string {
    return “(name: ” + p.name + ” – age: “+strconv.Itoa(p.age)+ ”
years)”
}

1.怎么着是反光

  • make、new操作

地点那么些办法的结合称为interface(被对象Student和Employee实现)。比方Student和Employee都落实了interface:SayHi和Sing,约等于那多个对象是该interface类型。而Employee未有达成那个interface:SayHi、Sing和BorrowMoney,因为Employee未有兑现BorrowMoney那个方法。

   空interface(interface{})不包罗其余的
method,正因为这么,全体的门类都落到实处了空interface。空 interface
对于描述起不到别的的功用(因为它不分包别的的 method),可是空interface
在大家需求仓库储存大肆档期的顺序的数值的时候一定有用,因为它能够积累任性等级次序的数值。贰个函数把interface{}作为参数,那么他得以承受大肆等级次序的值作为参数,假诺贰个函数重临interface{},那么也就足以回来大肆档期的顺序的值。是还是不是弹指间就认为interface很神奇!

也正是说,任何达成了String方法的种类都能当做参数被fmt.Println调用,让大家来试一试

 

  • 类型调换
    平安的不二秘技是使用以下格局来进展项目断言:

透过地点的代码,你会发觉interface正是一组抽象方法的聚焦,它必得由其他非interface类型达成,而无法自己完成,
Go通过interface达成了duck-typing:即”当看见贰头鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么那只鸟就能够被喻为鸭子”。

  作者们领略interface
的变量里面能够积存任性等级次序的数值(该类型完毕了interface)。那么大家怎么反向知情那一个变量里面其实保存了的是哪些品种的对象呢?近年来常用的有三种艺术:“Comma-ok
断言” 和“switch 测验”。

for _, n := range arg {
    fmt.Printf("And the number is: %d\n", n)
}

最棒的上课正是代码例子,将来让我们重写下面的这些完毕

  Go是静态类型语言。各个变量都有且唯有三个静态类型,在编写翻译时就曾经鲜明。尽管变量多个边路都有所协同的尾部数据类型,但它们的假若他们静态类型不雷同。不经过类型调换直接互动赋值时,编写翻译器会报错。相信大家通过一段纯熟的代码就活该知道了:

接口示例2

type Employee struct {
    Human //佚名字段Human
    company string
    money float32
}

三.空interface

*自定义类型
自定义类型不只是struct,struct只是自定义类型里面一种比较非常的种类而已,还大概有另外自定义类型申明,能够因而如下那样的阐述来贯彻。

注:实现了error接口的目的(即达成了Error()
string的靶子),使用fmt输出时,会调用Error()方法,因而不要再定义String()方法了。

  接口类型实际上是一组method(方法)签字的清单。interface
类型定义了一组方法,要是有些对象落成了有些接口的享有办法,则此目的就兑现了此接口。接口也是一种数据类型。假若你评释了贰个接口变量,这些变量能够存款和储蓄任何完成该接口的指标类型。最终,任意的项目都达成了空interface(大家这样定义:interface{}),也便是带有
0 个method的interface。所以自个儿欣赏给它起多个绰号叫“大胃王”。

i := 10
switch i {
case 1:
    fmt.Println("i is equal to 1")
case 2, 3, 4:
    fmt.Println("i is equal to 2, 3 or 4")
case 10:
    fmt.Println("i is equal to 10")
default:
    fmt.Println("All I know is that i is an integer")
}

复制代码 代码如下:

 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!

让我们透过一个事例来进一步深切的知情。

                                          作者:尹正杰

  • go func

    //i也能存储Employee
    i = Tom
    fmt.Println(“This is Tom, an Employee:”)
    i.SayHi()
    i.Sing(“Born to be wild”)