这是『就要学习 Go 语言』系列的第 19 篇分享文章

什么是接口

在一些面向对象的编程语言中,例如 Java、PHP 等,接口定义了对象的行为,只指定了对象应该做什么。行为的具体实现取决于对象。

在 Go 语言中,接口是一组方法的集合,但不包含方法的实现、是抽象的,接口中也不能包含变量。当一个类型 T 提供了接口中所有方法的定义时,就说 T 实现了接口。接口指定类型应该有哪些方法,类型决定如何去实现这些方法。

接口声明

接口的声明类似于结构体,使用类型别名且需要关键字 interface,语法如下:

type Name interface {Method1(param_list) return_typeMethod2(param_list) return_type...
}
复制代码

实际定义一个接口:

type Shape interface {Area() float32
}
复制代码

上面的代码定义了接口类型 Shape,接口中包含了一个不带参数、返回值为 float32 的方法 Area()。任何实现了方法 Area() 的类型 T,我们就说它实现了接口 Shape。

type Shape interface {Area() float32
}func main() {var s Shapefmt.Println("value of s is", s)fmt.Printf("type of s is %T\n", s)
}
复制代码

输出:

value of s is <nil>
type of s is <nil>
复制代码

上面的代码,由于接口是一种类型,所以可以创建 Shape 类型的变量 s,你是不是很疑惑 s 的类型为什么是 nil?让我们来看下一节!

接口类型值

静态类型和动态类型

变量的类型在声明时指定、且不能改变,称为静态类型。接口类型的静态类型就是接口本身。接口没有静态值,它指向的是动态值。接口类型的变量存的是实现接口的类型的值。该值就是接口的动态值,实现接口的类型就是接口的动态类型。

type Iname interface {Mname()
}type St1 struct {}
func (St1) Mname() {}
type St2 struct {}
func (St2) Mname() {}func main() {var i Iname = St1{}fmt.Printf("type is %T\n",i)fmt.Printf("value is %v\n",i)i = St2{}fmt.Printf("type is %T\n",i)fmt.Printf("value is %v\n",i)
}
复制代码

输出:

type is main.St1
value is {}
type is main.St2
value is {}
复制代码

变量 i 的静态类型是 Iname,是不能改变的。动态类型却是不固定的,第一次分配之后,i 的动态类型是 St1,第二次分配之后,i 的动态类型是 St2,动态值都是空结构体。

有时候,接口的动态类型又称为具体类型,当我们访问接口类型的时候,返回的是底层动态值的类型。

nil 接口值

我们来看个例子:

type Iname interface {Mname()
}
type St struct {}
func (St) Mname() {}
func main() {var t *Stif t == nil {fmt.Println("t is nil")} else {fmt.Println("t is not nil")}var i Iname = tfmt.Printf("%T\n", i)if i == nil {fmt.Println("i is nil")} else {fmt.Println("i is not nil")}fmt.Printf("i is nil pointer:%v",i == (*St)(nil))
}
复制代码

输出:

t is nil
*main.St
i is not nil
i is nil pointer:true
复制代码

是不是很惊讶,我们分配给变量 i 的值明明是 nil,然而 i 却不是 nil。 来看下怎么回事!

动态类型在上面已经讲过,动态值是实际分配的值。记住一点:当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。上面的代码,给变量 i 赋值之后,i 的动态值是 nil,但是动态类型却是 *St, i 是一个 nill 指针,所以相等条件不成立。

看下 Go 语言规范:

 var x interface{}  // x is nil and has static type interface{}var v *T           // v has value nil, static type *Tx = 42             // x has value 42 and dynamic type intx = v              // x has value (*T)(nil) and dynamic type *T
复制代码

通过这一节学习,相信你已经很清楚为什么上一节的 Shape 类型的变量的 s 输出的类型是 nil,因为 var s Shape 声明时,s 的动态类型是 nil。

实现接口

看示例:

type Shape interface {Area() float32
}type Rect struct {width  float32height float32
}func (r Rect) Area() float32 {return r.width * r.height
}func main() {var s Shapes = Rect{5.0, 4.0}r := Rect{5.0, 4.0}fmt.Printf("type of s is %T\n", s)fmt.Printf("value of s is %v\n", s)fmt.Println("area of rectange s", s.Area())fmt.Println("s == r is", s == r)
}
复制代码

输出:

type of s is main.Rect
value of s is {5 4}
area of rectange s 20
s == r is true
复制代码

上面的代码,创建了接口 Shape、结构体 Rect 以及方法 Area()。由于 Rect 实现了接口定义的所有方法,虽然只有一个,所以说 Rect 实现了接口 Shape。

在主函数里,创建了接口类型的变量 s ,值为 nil,并用 Rect 类型的结构体初始化,因为 Rect 结构体实现了接口,所以这是有效的。赋值之后,s 的动态类型变成了 Rect,动态值就是结构体的值 {5.0,4.0}。

可以直接使用 . 语法调用 Area() 方法,因为 s 的具体类型是 Rect,而 Rect 实现了 Area() 方法。

空接口

一个不包含任何方法的接口,称之为空接口,形如:interface{}。因为空接口不包含任何方法,所以任何类型都默认实现了空接口。

举个例子,fmt 包中的 Println() 函数,可以接收多种类型的值,比如:int、string、array等。为什么,因为它的形参就是接口类型,可以接收任意类型的值。

func Println(a ...interface{}) (n int, err error) {}
复制代码

我们来看个例子:

type MyString string
type Rect struct {width  float32height float32
}
func explain(i interface{}) {fmt.Printf("type of s is %T\n", i)fmt.Printf("value of s is %v\n\n", i)
}
func main() {ms := MyString("Seekload")r := Rect{5.0, 4.0}explain(ms)explain(r)
}
复制代码

输出:

type of s is main.MyString
value of s is Seekloadtype of s is main.Rect
value of s is {5 4}
复制代码

上面的代码,创建了自定义的字符串类型 MyString 、结构体 Rect 和 explain() 函数。explain() 函数的形参是空接口,所以可以接收任意类型的值。

关于接口使用的第一部分就讲到这,后续会再开文章给大家讲剩余部分,继续关注!

(全文完)

原创文章,若需转载请注明出处!
欢迎扫码关注公众号「Golang来啦」或者移步 seekload.net ,查看更多精彩文章。

公众号「Golang来啦」给你准备了一份神秘学习大礼包,后台回复【电子书】领取!

转载于:https://juejin.im/post/5c9d5631f265da60d82dde9c

Go 语言接口详解(一)相关推荐

  1. Python的C语言接口 - 详解官方文档

    Python的C语言接口 - 详解官方文档 索引 Python的C语言接口 - 详解官方文档 介绍 / Introduce 代码标准 / Coding Standards 包含文件 / Include ...

  2. 代码检查规则:Python语言案例详解

    在之前的文章中代码检查规则:Java语言案例详解学习了Java的检查规则.我们今天将学习<代码检查规则:Python语言案例详解>,内容主要分为两个部分:Python的代码检查规则和Pyt ...

  3. 12_Go语言 反射详解

    1. 反射定义: 可以在运行时动态获取变量的相关信息. Import ("reflect") 官方对此有个非常简明的介绍,两句话耐人寻味: 反射提供一种让程序检查自身结构的能力.再 ...

  4. 单片机 rs232 c语言,完全手册 51单片机C语言开发详解系列之第8章 串口通讯实战—RS232...

    完全手册 51单片机C语言开发详解系列 技术凝聚实力 专业创新出版 第8章 串口通讯实战-RS232接口电路与软件设计 本章主要通过实际的例子讲解AT89S51单片机的RS232串行通讯接口设计. 随 ...

  5. C#语言入门详解笔记(9)—P19 委托详解

    C#语言入门详解_哔哩哔哩_bilibiliC#语言入门详解搬运,随youtube上进度更新.刘老师的C#视频教程到30集就告一段落了,感谢刘老师在这5年间的付出.能上youtube的同学可以去刘老师 ...

  6. python traceback_Python语言Traceback详解

    本文主要向大家介绍了Python语言Traceback详解,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. 刚接触Python的时候,简单的异常处理已经可以帮助我们解决大多数问题, ...

  7. python语言编程基础-Python语言入门详解!快速学成Python!

    原标题:Python语言入门详解!快速学成Python! 很多技能是被职场所需要的,但很可惜... 这些技能在大学中并学习不到. 大学和职场现实存在的横沟对大部分同学来说难以跨越或碰得头破血流... ...

  8. python语言入门m-Python语言入门详解!快速学成Python!

    今日主题 "Python语言入门详解" 近两年来,Python语言借着数据科学和人工智能的"东风"成为了最流行的编程语言--街头巷尾人们口口相传.同时,Pyth ...

  9. Java6.0中Comparable接口与Comparator接口详解

    Java6.0中Comparable接口与Comparator接口详解 说到现在,读者应该对Comparable接口有了大概的了解,但是为什么又要有一个Comparator接口呢?难道Java的开发者 ...

最新文章

  1. python3菜鸟教程-总算理解python3中文入门教程
  2. mysql的事务操作
  3. origin图上显示数据标签_Origin(Pro):寒假都结束了,这个图还是不会画?【数据绘图】...
  4. C# 调用动态链接库读取二代身份证信息
  5. Oracle及Oracle客户端、PLSQL安装的一些问题
  6. Java基础常见的面试题
  7. visio 2013安装使用
  8. 我国政务大数据政策的文本分析:推进逻辑与未来进路
  9. Arduino nRF52840高级蓝牙5
  10. 爱玩软件 | win10桌面美化
  11. 从RTS游戏看游戏开发
  12. 七雄Q传封包辅助技术探讨回忆贴
  13. 16岁的雅虎问答,因“不再受欢迎”将永久关闭
  14. unity打开android界面报错,Unity3D与Android交互介绍.docx
  15. iccid的PHP规则,中国联通(U)SIM卡ICCID命名规则
  16. 晶圆检测显微镜是做什么研究才用到的?
  17. Android DRM框架分析
  18. linux终端玩fc游戏,在UBuntu下玩FC和街机模拟器
  19. netty之微信-群聊的发起与通知(十八)
  20. 飞檐走壁已经不是成龙甄子丹们的特技了,TA也可以做到!

热门文章

  1. 一些关于大数据的总结
  2. Delphi BLE 控件
  3. Python之冒泡排序和选择排序的比较
  4. Eltima SDK 中存在27个漏洞,影响多家云服务提供商
  5. 以色列证实上周末对伊朗核设施发动了网络攻击
  6. 2018.12.11 区块链论文翻译
  7. 互联网+工业,从哪里开始?
  8. 解决 Laravel/Lumen 出现 Please provide a valid cache path 问题
  9. BZOJ 3261: 最大异或和 [可持久化Trie]
  10. allegro 16.6 空心焊盘的制作