在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]接口的运用的更多相关文章

  1. App开发:模拟服务器数据接口 - MockApi

    为了方便app开发过程中,不受服务器接口的限制,便于客户端功能的快速测试,可以在客户端实现一个模拟服务器数据接口的MockApi模块.本篇文章就尝试为使用gradle的android项目设计实现Moc ...

  2. 干货来袭-整套完整安全的API接口解决方案

    在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优 在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对 ...

  3. 12306官方火车票Api接口

    2017,现在已进入春运期间,真的是一票难求,深有体会.各种购票抢票软件应运而生,也有购买加速包提高抢票几率,可以理解为变相的黄牛.对于技术人员,虽然写一个抢票软件还是比较难的,但是还是简单看看123 ...

  4. Java基础Map接口+Collections工具类

    1.Map中我们主要讲两个接口 HashMap  与   LinkedHashMap (1)其中LinkedHashMap是有序的  怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...

  5. Java基础Map接口+Collections

    1.Map中我们主要讲两个接口 HashMap  与   LinkedHashMap (1)其中LinkedHashMap是有序的  怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...

  6. java基础_集合List与Set接口

    List接口继承了Collection的方法  当然也有自己特有的方法向指定位置添加元素   add(索引,添加的元素); 移除指定索引的元素   remove(索引) 修改指定索引的元素   set ...

  7. 【WCF】自定义错误处理(IErrorHandler接口的用法)

    当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...

  8. PHP以接口方式实现多重继承(完全模拟)--学习笔记

     1.UML类图: 2.PHP代码: <?php /** * Created by PhpStorm. * User: andy * Date: 16-11-23 * Time: 下午7:57 ...

  9. 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo

    Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...

  10. 【接口开发】浅谈 SOAP Webserver 与 Restful Webserver 区别

    接口,强大,简单,交互,跨越平台 下面简单阐述这两大接口思想 一 REST: REST是一种架构风格,其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性. ...

随机推荐

  1. getAttribute()方法的第二个参数

    对于一个img元素,我们想获取它的src属性时可以有两种方式: 1.xxx.getAttribute("src") 2.直接通过xxx.src获取属性值 在src的属性值为相对路径 ...

  2. php Try Catch多层级异常测试

    <?php class a { public function a1 () { try { throw new Exception('123'); } catch (Exception $e) ...

  3. 作用域链、this细说

    一.作用域链 作用域:浏览器给js的一个生存环境(栈内存) 作用域链:js中的关键字var和function 都可以提前声明和定义,提前声明和定义的放在我们的内存地址(堆内存)中.然后js从上到下逐行 ...

  4. 通过HTML 取得页面、屏幕、浏览器的高度宽度

    一.介绍 1. 容器 一个页面的展示,从外到内的容器为:屏幕.浏览器以及页面本身. HTML元素展现在页面内,页面展现在浏览器内,而浏览器展现在屏幕内. 通过Js的一些对象可以获取这些容器的高度.宽度 ...

  5. Redis学习笔记(四)集合进阶

    1.组合与关联多个集合 差集: SDIFF key1 [key2...](返回存在于key1但不存在其他集合中的元素) SDIFFSTORE destination key1 [key2...](将存 ...

  6. Angular和SAP C4C的事件处理队列

    Angular 我们在Angular框架的代码里能看到一个名为processQueue的函数: 这个函数是通过$scope.$apply启动的: 核心代码位于一个for循环里,循环体是一个存储异步处理 ...

  7. SEO 第二章

    SEO第二章 1.  掌握搜索引擎工作原理(重点) 2.  了解百度算法 3.  关键词的分类 一.什么是搜索引擎? 搜索引擎是用来实现搜索服务的,说白了搜索引擎也属于一种网站. 浏览器是用来加载网站 ...

  8. Python3中的输入输出

    input()函数 我们可以通过Python3解释器查看Python3中input()的含义: >>> type(input) <class 'builtin_function ...

  9. fgetpos, fseek, fsetpos, ftell, rewind - 重定位某个流

    总览 (SYNOPSIS) #include <stdio.h> int fseek(FILE *stream, long offset, int whence); long ftell( ...

  10. 使用python编写的简单远程管理软件

    因为用户可以选择是否同意被控制,所以并不算是木马. 使用python3.7,spyder,在windows 10 开发. client为控制端,server为被控端. 参考 mygithub http ...