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 ...
随机推荐
- 【Python】分享使用的插件文件链接(实时更新)
链接:https://pan.baidu.com/s/1o7AgHtw Python工具实时更新.
- Link-based Classification相关数据集
Link-based Classification相关数据集 Datasets Document Classification Datasets: CiteSeer: The CiteSeer dat ...
- C++ STL标准模板库(queue)
//queue的使用 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<queue> using name ...
- Java释出的时候,AWT作为Java最弱的组件受到不小的批评
Java释出的时候,AWT作为Java最弱的组件受到不小的批评. 最根本的缺点是AWT在原生的用户界面之上仅提供了一个非常薄的抽象层. 例如,生成一个AWT的 复选框会导致AWT直接调用下层原生例程来 ...
- java网络编程4-ServerSocket
//端口号为0则系统随机分配端口,连接队列系统一般默认50,指过超过系统最大的就以系统为准 //如果客户端的连接超过连接队列,则会被主机拒绝 ServerSocket serverSocket=new ...
- HTML DOM和BOM常用操作总结
JavaScript Code 1234567891011121314151617181920212223242526272829303132333435363738394041424344454 ...
- 拦截器(Inteceptor),过滤器(Filter),切面(Aspect)处理HttpServiceReqeust请求
1.拦截器 java里的拦截器是动态拦截Action调用的对象.它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可 ...
- SurvivalShooter学习笔记(六.玩家生命)
需求: 玩家有初始生命: 被敌人攻击后:掉血,播放受击音效,红屏(用UI图片做)闪烁提示,UI面板刷新生命 直至死亡:死亡播放死亡音效,游戏结束: 1.变量: 玩家生命 public int star ...
- Python实现生命游戏
1. 生命游戏是什么 生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机.它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个 ...
- 用pypy运行ryu
最近看到pypy可以提高python的运行速率到很变态的境地,加之现在ryu发现拓扑的能力有限,不能满足实验要求,所以想将其试着在pypy上运行 部署pypy在virtualenv,在学python初 ...