[Go]接口的运用
在Go语言中,不能通过调用new函数或make函数创建初一个接口类型的值,也无法用字面量来表示一个接口类型的值。可以通过关键字type和interface声明接口类型,接口类型的类型字面量与结构体类型有些相似,不过结构体类型包裹的是它的字段声明,而接口类型包裹的是它的方法定义。
接口类型声明中的这些方法所代表的就是该接口的方法集合,一个接口的方法集合就是它的全部特征。对任何数据类型,只要它的方法集合中完全包含了一个接口的全部方法,那么它就一定是这个接口的实现类型。
type Pet interface {
SetName(name string)
Name() string
Category() string
}
这里声明了一个接口类型Pet,它包含了3个方法定义,这3个方法共同组成了接口类型Pet的方法集合,只要一个数据类型的方法集合中有这3个方法,那么它就一定是Pet接口的实现类型。这是一种无侵入式的接口实现方式。
那怎样判定一个数据类型的某个方法实现的就是某个接口类型中的某个方法呢?有两个充分必要条件:
1)两个方法的签名需要完全一致
2)两个方法的名称要一模一样
type Dog struct {
name string // 名字。
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func (dog Dog) Name() string {
return dog.name
}
func (dog Dog) Category() string {
return "dog"
}
Dog类型本身的方法集合中只包含了2个值方法,而它的指针类型*Dog方法集合却包含了3个方法(两个值方法加一个指针方法),又由于这3个方法恰恰分别是Pet接口中某个方法的实现,所以*Dog类型就成为了Pet接口的实现类型。
dog := Dog{"little pig"}
var pet Pet = &dog
因此可以声明并初始化一个Dog类型的变量dog,然后把它的指针值赋给类型为Pet的变量pet。
对于一个接口类型的变量,赋给它的值可以被叫做它的实际值,而该值的类型可以被叫做这个变量的实际类型(动态类型)
如把&dog的结果值赋给了变量pet,这个结果值就是变量pet的动态值,*Dog就是该变量的动态类型。对于变量pet来说,它的静态类型就是Pet,并且永远是Pet,但是它的动态类型会随着我们赋给它的动态值而变化,并且在给一个接口类型的变量赋予实际的值之前,它的动态类型是不存在的。
1、当为一个接口变量赋值时会发生什么?
假设把Pet接口声明简化一下
type Pet interface {
Name() string
Category() string
//去掉了SetName方法
}
这时
dog := Dog{"little pig"}var pet Pet = dog
dog.SetName("monster")
这时pet变量的字段name的值依然时”little pig“
由于dog的SetName方法时指针方法,所以该方法的持有的接收者就是指向dog的指针值的副本,因而其中对接收者的name字段的设置就是对变量dog的改动。那么当dog.SetName执行之后,dog的name字段的值就一定是”monster“。
那为什么dog的name变量,而pet却没有呢?
如果使用一个变量给另外一个变量赋值,那么真正赋给后者的,并不是前者持有的那个值,而是该值的一个副本。 例如
dog1 := Dog{"litte dog"}
dog2 := dog1
dog1.name = "monster"
此时dog2的值仍然回收”litter dog“
接口类型本身时无法被值化的,在赋予它实际的值之前,它的值一定会是nil,这是它的零值。
当给接口变量赋值时,该变量的动态类型会与它的动态值一起被存储在一个专用的数据结构中。这样的一个变量的值其实时这个专用数据结构的一个实例,而不是我们赋给该变量的那个实际的值。所以pet的值与dog值肯定是不同的,无论从它们存储的内容还是存储的结构。
Go语言把这个专用的数据结构叫做iface,iface会包含两个指针,一个时指向类型信息的指针,另一个时指向动态值的指针。这里的类型信息由另一个专用数据结构的实例承载,其中包含了动态值的类型,以及使它实现了接口的方法和调用它们的途径。
总之,接口变量被赋予动态值的时候,存储的是包含这个动态值的副本的一个结构更加复杂的值。
2、接口变量的值在什么情况下才真正为nil?
var dog1 *Dog
fmt.Println("The first dog is nil.")
dog2 := dog1
fmt.Println("The second dog is nil.")
var pet Pet = dog2
if pet == nil {
fmt.Println("The pet is nil.")
} else {
fmt.Println("The pet is not nil.")
}
dog1和dog2是nil很好理解,那pet会是nil吗?
pet的值不会是nil,因为这个动态值只是pet值的一部分。此时pet动态类型是*Dog
把nil赋值给了pet,但pet的值却不是nil,这不是很奇怪吗?
在Go语言中,把字面量nil表示的值叫做无类型的nil,这是真正的nil,因为它的类型也是nil。虽然dog2的值是真正的nil,但当我们把这个变量赋给pet的时候,Go语言会把它的类型和值放在一起考虑。即此时Go语言会识别初赋予pet的值是一个*Dog类型的nil,然后Go语言会用一个iface实例包装它,包装后的产物肯定就不是nil了。
因而只要把一个有类型的nil赋给接口变量,那这个变量的值就一定不会是真正的nil。
那要怎样才能让一个接口变量的值真正为nil呢?要么只声明它当不做初始化,要么之间把字面量nil赋给它。
3、怎样实现接口之间的组合?
接口类型间的嵌入被称为接口的组合。接口的组合不会出现屏蔽现象
type Animal interface {
// ScientificName 用于获取动物的学名。
ScientificName() string
// Category 用于获取动物的基本分类。
Category() string
}
type Named interface {
// Name 用于获取名字。
Name() string
}
type Pet interface {
Animal
Named
}
[Go]接口的运用的更多相关文章
- App开发:模拟服务器数据接口 - MockApi
为了方便app开发过程中,不受服务器接口的限制,便于客户端功能的快速测试,可以在客户端实现一个模拟服务器数据接口的MockApi模块.本篇文章就尝试为使用gradle的android项目设计实现Moc ...
- 干货来袭-整套完整安全的API接口解决方案
在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优 在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对 ...
- 12306官方火车票Api接口
2017,现在已进入春运期间,真的是一票难求,深有体会.各种购票抢票软件应运而生,也有购买加速包提高抢票几率,可以理解为变相的黄牛.对于技术人员,虽然写一个抢票软件还是比较难的,但是还是简单看看123 ...
- Java基础Map接口+Collections工具类
1.Map中我们主要讲两个接口 HashMap 与 LinkedHashMap (1)其中LinkedHashMap是有序的 怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...
- Java基础Map接口+Collections
1.Map中我们主要讲两个接口 HashMap 与 LinkedHashMap (1)其中LinkedHashMap是有序的 怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...
- java基础_集合List与Set接口
List接口继承了Collection的方法 当然也有自己特有的方法向指定位置添加元素 add(索引,添加的元素); 移除指定索引的元素 remove(索引) 修改指定索引的元素 set ...
- 【WCF】自定义错误处理(IErrorHandler接口的用法)
当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...
- PHP以接口方式实现多重继承(完全模拟)--学习笔记
1.UML类图: 2.PHP代码: <?php /** * Created by PhpStorm. * User: andy * Date: 16-11-23 * Time: 下午7:57 ...
- 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo
Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...
- 【接口开发】浅谈 SOAP Webserver 与 Restful Webserver 区别
接口,强大,简单,交互,跨越平台 下面简单阐述这两大接口思想 一 REST: REST是一种架构风格,其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性. ...
随机推荐
- RHEL 6.5----heartbeat
主机名 IP 所需软件 master 192.168.30.130 heartbeat.httpd node-1 192.168.30.131 nfs node-2 192.168.30.1 ...
- 在solr客户端删除库中的数据
1.在solr客户端,访问你的索引库(我认为最方便的方法) 1)documents type 选择 XML 2)documents 输入下面语句<delete><query>* ...
- Spark MLlib编程API入门系列之特征选择之向量选择(VectorSlicer)
不多说,直接上干货! 特征选择里,常见的有:VectorSlicer(向量选择) RFormula(R模型公式) ChiSqSelector(卡方特征选择). VectorSlicer用于从原来的特征 ...
- Suricata的Reputation
见官网 https://suricata.readthedocs.io/en/latest/reputation/index.html Docs » 9. Reputation Edit on Git ...
- 【C#】.net 发送get/post请求
基础学习 /// <summary> /// Http (GET/POST) /// </summary> /// <param name="url" ...
- idea使用Git提交代码时忽略指定文件或文件夹
简述 使用idea编写代码并使用git作为版本控制器的时候,常常不需要提交配置文件以及一些其他不需要提交的文件,可以使用.ignore插件来在上传的时候忽略一些文件或文件夹. 安装 注意:安装完成之后 ...
- elasticsearch6安装head插件
1.head 插件Github地址:https://github.com/mobz/elasticsearch-head 2.npm install 3.npm run start 由于head插件监 ...
- Redis java操作客服端——jedis
1. Jedis 需要把jedis依赖的jar包添加到工程中.Maven工程中需要把jedis的坐标添加到依赖. 推荐添加到服务层.happygo-content-Service工程中. 1.1. 连 ...
- 2019/05/13 JAVA虚拟机堆内存调优
-Xms4000m 堆内存初始值 * -Xmx4000m 堆内存最大值 * -XX:+PrintGCDetails 打印GC信息 * -XX:+UseSerialGC 使用串行GC * -XX:+Pr ...
- shutil模块详解2
1.shutil.make_archive() 实际上是调用了两个模块来实现压缩打包的功能. zipfile和tarfile两个模块,shutil的两个封装的模块. zip是压缩文件,文件内存会变小, ...