第五章 接口

第五章 接口,第1张

概述第一天: 接口的定义和实现 第二天: 一. go语言是面向接口编程. 在学习继承的时候说过, go语言只有封装, 没有继承和多态. 那么继承和多态在go中如何实现呢? 通过接口来实现 1. 接口的定义

第一天: 接口的定义和实现

第二天:

一. go语言是面向接口编程.

  在学习继承的时候说过,go语言只有封装,没有继承和多态. 那么继承和多态在go中如何实现呢? 通过接口来实现

1. 接口的定义

接口定义由两部分组成. 使用者和实现者. 

 

 

 接口由使用者定义是什么意思呢?

比如,我要下载一个资源. 我定义一个download方法. 这个方法是谁使用谁定义.

func download(r RetrIEver) string {    return r.Get("http://www.baIDu.com")}

给方法传了一个参数r RetrIEver. 这个参数可以理解为java中的泛型. 我们将它定义成一个接口. 然后谁用他,谁来实现它. 但他总体的方法是调用接口中的Get

我们来定义一个接口

type RetrIEver interface {    Get(s string) }

这就是接口的定义. 接口里面的方法不用写func

 

然后我们来调用这个接口试一试

func main() {    var r RetrIEver    fmt.Println(download(r))}

使用者在调用的时候,直接调用down就可以了. 这里会返回异常,因为r是一个空指针

 

2. 接口的实现 

接下来我们来创建一个新的包mock,然后写一个接口的实现. 

package mocktype RetrIEver struct {    Contents }func (r RetrIEver) Get(c {    return r.Contents}

 

这就是对RetrIEver的实现. 这里的RetrIEver和接口同名. 他们在不同的包中. 然后实现了Get方法. 这样就完成了对RetrIEver接口的一个实现. 

很奇怪,这和java不同,里面没有出现任何实现关键字,也没有出现接口关键字. 

注意: go中接口实现,只要定义的方法名,方法参数和返回值和接口定义的一致,就认为他是对接口的一个实现. 在开发工具中就会自动识别出来

 

 

我们再来写一下实现类

func main() {    var r RetrIEver    r = mock.RetrIEver{"this is a fake www.baIDu.com"}    .Println(download(r))}

初始化的时候是初始化的接口,调用的时候,调的是实现类

这是一个模拟假的download方法,返回值如下

 

 

我们再来写一个实现类,真正的下载方法. 比如我们下载百度的首页面. http://www.baIDu.com

首先创建一个real文件夹,然后新建一个文件Real

package realimport (    net/http"    net/http/httputiltime)type Real struct {    url     downTime time.Duration}func (r Real) Get(url string) string {    resp,err := http.Get(url)    if err != nil {        panic(err)    }    result,err := httputil.DumpResponse(resp,true)     nil {        panic(err)    }    resp.Body.Close()    return (result)}

定义了一个方法Get(url string) string .只要一个结构体定义了这个Get方法,入参和出参和接口定义保持一致,go就认为这个结构体实现了RetrIEver接口

我们来看看具体的调用

func main() {    var r RetrIEver    r = mock.RetrIEver{this is a fake www.baIDu.com}    .Println(download(r))    r = real.Real{}    fmt.Println(download(r))}

这样就调用了接口的第二个实现real.Real

 

问题: r变量被两次赋值,那么他到底是什么类型呢?

3. 接口如何实现值传递和指针传递
func (r Real) Get(url string) string 使用只传递的方式

以上方法都是使用值传递,那么,如果一个对象很大,我们不想用值传递,也可以使用指针传递.

func (r *Real) Get(url string) string 使用指针传递

那么接收者如何接收呢? 传递过来的是指针. 那么我们接收的时候,就要接收一个地址

.Println(download(r))    r = &real2.Real{"Google",100}    fmt.Println(download(r))}

否则会报异常. 

总结: 定义get方法的时候,前面传的是值(r Real),那么这个对象就是以值拷贝的方式传递. 如果前面传的值时(r *Real),那么这个结构体就以地址拷贝的方式传递

4. 判断接口的类型方法一: 通过fmt.Printf("%T %v \n",r,r)打印的方式法二: 通过type switch方法三: 通过type assertion

方法一: 通过fmt.Printf("%T %v \n",r)打印的方式

}    fmt.Printf("%T,%v \n",r)    .Println(download(r))    r = real2.Real{}    //fmt.Println(download(r))}

结果如下:

mock.RetrIEver,{this is a fake www.baIDu.com} this is a fake www.baIDu.comreal.Real,{ 0}

 

方法二: 通过type switch

}    judgeType(r)    .Println(download(r))    r = &real2.Real{Google",100fmt.Println(download(r))}func judgeType(r RetrIEver) {    switch r.(type) {    case mock.RetrIEver:        fmt.Println(content:,r.(mock.RetrIEver).Contents)    case *real2.Real:        ua:real2.Real).Ua)    }}

 

方法三: 通过type asserttion

type asserttion就是类型转换. 

}    judgeType(r)    }    judgeType(r)    realRetrIEver := r.(*real2.Real)    fmt.Println(realRetrIEver)    // 那么转换可能会失败,失败使用另外一个参数ok来判断    if rr,ok := r.(mock.RetrIEver); ok {        fmt.Println(rr)    }    fmt.Println(download(r))}
r.(*real2.Real)这种类型转换的方式就是type assertion. 他也可以用来判断类型. 但是他可能会转换失败. 我们可以如下用法:
if rr,ok := r.(mock.RetrIEver); ok {        .Println(rr) }

这就是将一个对象进行类型判断的三种方式.

接口不是简单的引用,接口的肚子里还有两个东西,一个是类型,一个是值

总结:

接口变量包含哪些东西呢?

1. 实现者的类型

2. 实现者的值或者地址指针,实现者的指针最终指向的也是实现者的值

  或者 

 

 第三点: 指针接收者实现只能以指针方式使用; 值接收者都可以. 这句话的含义是,实现接口方法的时候,接收者是指针类型,那么在构建结构体的时候,只能是指针的方式. 但如果接收者是值传递的方式,那么结构体可以使用指针接收也可以使用值接收

例如: 

}func (r RetrIEver) Get(c {    return r.Contents}

这是一个值接收者. 

 使用的时候可以这样使用:

func main() {    var r RetrIEver    r = mock.RetrIEver{}    r = &mock.RetrIEver{}}

如果是指针接收者,那么调用的时候只能使用指针调用

)type Real struct {    Ua     TimeOut .Duration}func (r *Real) Get(url (result)}
func main() {    var r RetrIEver        r = &real2.Real{}    }
 5. 任何类型用interface{}表示.6. 接口的组合 

我们上面有了一个下载的方法. 还有一个上传的方法. 怎么写呢? 写法和download是一样的. 上传有上传的url和内容

 在定义一个函数postfunc post(p Poster) {    p.Post(string]{        contents":提交的内容nameaaa 定义了另外一个接口,提交者Postertype Poster interface {    Post(url }

到这里,都和download的定义方法一样. 

我们来给post接口一个实现, 在mock.RetrIEver结构体中实现Post

func (r *RetrIEver) Post(url  {    r.Contents = from[]    return ok}

 

 

 这里mock.RetrIEver结构体就实现了两个接口. Get方法实现了RetrIEver接口,Post实现了Poster接口. 

 

那么我们说接口是由使用者来定义的. 那么使用者还有一个需求,那就是,我就想能够上传也能够下载. 比如有一个session. 

已经有了上传接口了,也有下载接口了,那么这时候要技能上传又能下载,怎么办呢?总不能在重写一份吧. 在java里,我们是怎么做的呢? 再写一个接口,实现上传和下载这两个接口.

而在go里我们要使用的是接口的组合.

 然后又定义了一个session,他既能缓存RetrIEver,也能缓存Postertype RetrIEverPoster interface {     这个时候只需要实现上面两个接口即可    RetrIEver    Poster} 我还想有一个功能,既能上传也能下载,需要定义一个新的接口.func session(r RetrIEverPoster) {    r.Post(": another fake a address)}

然后

func main() {    retrIEver := &mock.RetrIEver{"内容"}    .Println(session(retrIEver))}

调用的时候,我定义的mock.RetrIEver{}结构体能够直接当做RetrIEverPoster类型来使用

当吧retrIEver作为参数传给session的时候,编译器能够知道retrIEver是结构体mock.RetrIEver的变量,而结构体mock.RetrIEver实现了Get和Post方法

所以,可以吧retrIEver直接作为参数传给session

 7. 组合接口在go sdk中的应用

例如: ReaDWrite相关的接口

 

 和ReaDWrite相关的接口有四个. 这些都是描述他们的能力的 可读可写. 来看看他们是如何实现的

 

 

 

 

 

以上: 都是采用的接口组合的方式 

 

以下是完整的代码

1. 接口定义

package mainimport (    aaa/retrIEver/mock    real2 aaa/retrIEver/realfmt) 1. 定义一个接口}func download(r RetrIEver) )}func main() {    var r RetrIEver    r = mock.RetrIEver{ real2.Real{}    .Println(download(r))}

2. mock中第一个实现接口的结构体

{    return r.Contents}

3. real中第二个实现接口的结构体

.Duration}func (r Real) Get(url (result)}

 

 

 

 

 

   

总结

以上是内存溢出为你收集整理的第五章 接口全部内容,希望文章能够帮你解决第五章 接口所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1254719.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存