go——接口(二)
多态是指代码可以根据类型的具体实现采取不同行为的能力。
如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值。
标准库里有很好的例子,如io包里实现的流式处理接口。
io包提供了一组构造得非常好得接口和函数,来让代码轻松支持流式数据处理。
只要实现两个接口,就能利用整个io包背后得所有强大能力。
1.标准库
先来看一个示例程序,这个程序实现了流行程序curl得功能。
示例1:
//这个示例程序展示如何使用io.Reader和io.Writer接口
//写一个简单版本得curl程序
package main import (
"fmt"
"io"
"net/http"
"os"
) //init在main函数之前调用
func init() {
if len(os.Args) != 2 {
fmt.Println("Usage: ./example<url>")
os.Exit(-1)
}
} //main是应用程序得入口
func main() {
//从web服务器得到响应
r, err := http.Get(os.Args[1])
if err != nil {
fmt.Println(err)
range
} //从Body复制到Stdout
io.Copy(os.Stdout, r.Body)
if err := r.Body.Close(); err != nil {
fmt.Println(err)
}
}
上述代码展示了接口的能力以及在标准库里的应用。
只用了几行代码我们就通过两个函数以及配套的接口,完成了curl程序。
下面是部分代码的解释:
r, err := http.Get(os.Args[1])
调用了http包的get函数。在与服务器成功通信后,http.Get函数会返回一个http.Response类型的指针。
http.Response类型包含一个名为Body的字段,这个字段是一个io.ReadCloser接口类型的值。
io.Copy(os.Stdout, r.Body)
Body字段作为第二个参数传给io.Copy函数。io.Copy函数的第二个参数接受一个io.Reader接口类型的值,这个值表示数据流入的源。
Body字段实现了io.Reader接口,因此我们可以将Body字段传入io.Copy,使用Web服务器的返回内容作为源。
io.Copy的第一个参数是复制到目标,这个参数必须是一个实现了io.Writer接口的值。
对于这个目标,我们传入os包里的一个特殊值Stdout。这个接口值表示标准输出设备,并且已经实现了io.Writer接口。
当我们将Body和Stdout这两个值传给io.Copy函数后,这个函数会把服务器的数据分成小段,
源源不断地传给终端窗口,直到最后一个片段读取并写入到终端,io.Copy函数才返回。
io.Copy函数可以以这种工作流的方式处理很多标准库里已有的类型。
示例2:
//这个示例程序展示bytes.Buffer也可以
//用io.Copy函数
package main import (
"bytes"
"fmt"
"io"
"os"
) //main是应用程序的入口
func main() {
var b bytes.Buffer //将字符串写入Buffer
b.Write([]byte("Hello")) //将字符串拼接到Buffer
fmt.Fprintf(&b, "World!") //将Buffer的内容写到Stdout
io.Copy(os.Stdout, &b)
}
这个程序使用接口来拼接字符串,并将数据以流的方式输出到标准输出设备。
var b bytes.Buffer
创建一个bytes包里的Buffer类型的变量b,用于缓冲数据。
b.Write([]byte("Hello"))
使用Write方法将字符串Hello写入到缓冲区b。
fmt.Fprintf(&b, "World!")
调用fmt包里的Fprintf函数,将第二个字符串追加到缓冲区b里。
io.Copy(os.Stdout, &b)
将字符写道终端。
fmt.Fprintf函数接受一个io.Writer类型的接口值作为其第一个参数。
由于bytes.Buffer类型的指针实现了io.Writer接口,所以可以将缓存b传入fmt.Fprintf函数,并执行追加操作。
由于bytes.Buffer类型的指针也实现了io.Reader接口,io.Copy函数可以用于在终端窗口显式缓冲区b的内容。
2.实现
接口是用来定义行为的类型。这些被定义的行为不由接口直接实现,而是通过方法由用户定义的类型实现。
如果用户定义的类型实现了某个接口声明的一组方法,那么这个用户定义的类型的值可以赋给这个接口类型的值。
这个赋值会把用户定义的类型的值存入接口类型的值。
对接口值方法的调用会执行接口值里存储的用户定义的类型的值对应的方法。
因为任何用户定义的类型都可以实现任何接口,所以对接口值方法的调用自然就是一种多态。
在这个关系里,用户定义的类型通常叫做实体类型,原因是如果离开了内部存储的用户定义的类型的值的实现,接口值并没有具体的行为。
并不是所有值都完全等同,用户定义的类型的值或者指针要满足接口的实现,需要遵守一些规则。

上图展示了在user类型值赋值后接口变量的值的内部布局。
接口值是一个两个字长度的数据结构,第一个字包含一个指向内部表的指针。
这个内部表叫做iTable,包含了所存储的值的类型信息。
iTable包含了已存储的值类型信息以及与这个值相关联的一组方法。
第二个字是一个指向所存储值得指针。将类型信息和指针组合在一起,就将这两个值组成了一种特殊得关系。

上图展示了一个指针赋值给接口之后发生的变化。
在这种情况里,类型信息会存储一个指向保存的类型的指针,而接口值第二个字依旧保存指向实体值的指针。
3.方法集
package main import (
"fmt"
) //notifier是一个接口定义
//通知类行为的接口
type notifier interface {
notify()
} //user在程序里定义了一个用户类型
type user struct {
name string
email string
} //notify是使用指针接收者实现的方法
func (u *user) notify() {
fmt.Printf("Sending user email to %s<%s>\n", u.name, u.email)
} func main() {
//创建一个user类型的值,并发送通知
u := user{"Bill", "bill@email.com"} sendNotification(&u) }
//声明了一个函数,这个函数接收一个notifier接口类型的值
//之后使用这个接口值来调用notify方法。
//任何一个实现了notifier接口的值都可以传入sendNotification函数。
func sendNotification(n notifier) {
n.notify() //Sending user email to Bill<bill@email.com>
}
方法集定义了一组关联到给定类型的值或指针的方法。
定义方法时使用的接收者的类型决定了这个方法是关联到值还是关联到指针,还是两者都关联。

T类型的值的方法集只包含值接收者声明的方法。
而指向T类型的指针的方法集既包含值接收者声明的方法,也包含指针接收者声明的方法。
如果从接受者的视角来看。

如果使用执政接收者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。
如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。
go——接口(二)的更多相关文章
- Hadoop序列化与Writable接口(二)
Hadoop序列化与Writable接口(二) 上一篇文章Hadoop序列化与Writable接口(一)介绍了Hadoop序列化,Hadoop Writable接口以及如何定制自己的Writable类 ...
- 【C#夯实】我与接口二三事:IEnumerable、IQueryable 与 LINQ
序 学生时期,有过小组作业,当时分工一人做那么两三个页面,然而在前端差不多的时候,我和另一个同学发生了争执.当时用的是简单的三层架构(DLL.BLL.UI),我个人觉得各写各的吧,到时候合并,而他觉得 ...
- C#二次开发BIMFACE系列61 File Management文件管理服务接口二次开发及实战详解
系列目录 [已更新最新开发文章,点击查看详细] 在我的博客<C#二次开发BIMFACE系列61 File Management文件管理服务接口二次开发及实战详解>最后列出了 Fil ...
- java servlet手机app访问接口(二)短信验证
今天找了几个短信平台,其实最想使用的一个是sharesdk,使用它上面http api短信功能,不仅价格低,而且最少可以充值100RMB,但是审核过于严格,对应APP还必须集成他们的短信功能,而且要上 ...
- 一、HttpServletRequest接口 二、HttpServletReponse接口 三、POST和GET请求方式及其乱码处理 四、ServletContext对象和ServletConfig对象
一.HttpServletRequest接口 内部封装了客户端请求的数据信息 接收客户端的请求参数.HTTP请求数据包中配置参数 ###<1>常用方法 getContextPath()重要 ...
- java List接口二
一 ArrayList集合 ArrayList集合数据存储的结构是数组结构.元素增删慢,查找快,由于日常开发中使用最多的 功能为查询数据.遍历数据,所以ArrayList是最常用的集合. 许多程序员开 ...
- java 接口二
一 接口的多实现 接口最重要的体现:解决多继承的弊端.将多继承这种机制在java中通过多实现完成了. interface Fu1 { void show1(); } interface Fu2 { v ...
- Asp.NetCoreWebApi图片上传接口(二)集成IdentityServer4授权访问(附源码)
写在前面 本文地址:http://www.cnblogs.com/yilezhu/p/9315644.html 作者:yilezhu 上一篇关于Asp.Net Core Web Api图片上传的文章使 ...
- 对RedisTemplate接口二次封装成自定义工具接口
开发过程中,经常使用redis数据库存储. 一般都是依赖注入redisTemplate,然后调用redisTemplate的api进行接口功能实现. 但是,如果对redisTemplate自带的API ...
- go语言之接口二
接口查询: 先来看如下的结构.结构体File实现了Read,Writer,Seek,Close的方法 type File struct{ } func (f *File) Read(buf []byt ...
随机推荐
- Paxos发展、算法原理
Paxos 发展史 Leslie Lamport所提出的Paxos算法是现代分布式系统中的一项重要的基础性技术,得到广泛的应用. Paxos的整个发展过程大概可以分为三个阶段: 第一阶段:萌芽期,大致 ...
- 从git中更新本地需要填写的正则
<IfModule mod_rewrite.c> Options +FollowSymlinks RewriteEngine On RewriteCond %{REQUEST_FILENA ...
- 第二百零四节,jQuery EasyUI,Dialog(对话框)组件
jQuery EasyUI,Dialog(对话框)组件 学习要点: 1.加载方式 2.属性列表 3.事件列表 4.方法列表 本节课重点了解EasyUI中Dialog(窗口)组件的使用方法,这个组件依赖 ...
- JavaWeb——监听器
监听器简介 监听器是指专门用于在其他对象身上发生的事件或者状态改变进行监听和相应处理的对象,当被监听的对象发生变化时立即采取相应的行动. 例如我们要实现统计一个网站的在线人数,就可以在Web应用应用程 ...
- The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Cha
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Cha ...
- 【Raspberry Pi】crontab 定时任务
在linux上做定时任务一般用crond 两种方法上文已列,但昨天写的crond命令却一直都没有运行,上网查,有说是环境变量的,也有说是时间问题的,都改过,但还没有效. 今天再次认真读了一遍cront ...
- CSS样式呈现优先级
经常出现CSS样式不生效的问题. 比如我先对p{},然后在其中一个标签写了个.p_style{}怎么都不生效,于是想到了CSS的呈现优先级. 经过自己测试, 属性style="" ...
- asscert断言的几种方法
一.什么是断言 执行完测试用例后,最后一步是判断测试结果是通过还是失败,在自动化脚本中一般把这种生成测试结果的方法叫做断言 它用来检查一个条件,如果它为真,则不做任何事,如果它为假,则会跑出Asser ...
- linq依据传入数据集合查询相应子级数据
工作中经经常使用到的linq依据传入数据集合查询相应子级数据,整理共享,希望大家都能用得上,代码中doublesArray 为父节点相应ID数据集合,再依据ID数据集合查询所有子级数据. //获取缓存 ...
- 7.15;linux命令
麦克维瀑布 https://farm5.staticflickr.com/4269/34749113172_d6c1ba274a_k.jpg ----------------------------- ...