interface引发的事件真相
package main
import (
"fmt"
)
type People interface {
Name() string
}
type Student struct{ name string }
func (stu *Student) Name() string {
return stu.name
}
func getPeople() People {
var stu *Student
return stu
}
func main() {
if getPeople() == nil {
fmt.Println("AAAAA")
} else {
fmt.Println("BBBBB")
}
}
上面的代码输出什么那?有些人会认为打印AAAAA,因为他们会认为getPeople方法里面stu是nil 所以返回的就是nil,这样想就大错特错,因为虽然返回的stu是nil 但是函数返回时People接口的结构的本身并不是nil,在我们不了解interface内部结构之前请往下看。
为什么我会选择去写一个关于interface的文章那,我认为他在go语言里面有这非常重要的地位,仅次于goroutine和channel的地位,我在未接触go之前一直从事于c#的开发,接口对我来说就是不同组件之间的契约,对这个契约强制你必须去继承接口,而go语言的设计就非常轻巧,只要实现了接口所要求的所有函数即可,go中的接口分为两种一种是空的接口类似这样:
- var in interface{}
例外一种是非空的接口即在接口内部声明了一些方法:
type People interface {
Name() string
}
接下来我就根据上面的例子来对比一下空接口和非空接口内部结构
type eface struct { //空接口
_type *_type //类型信息
data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
type iface struct { //带有方法的接口
tab *itab //存储type信息还有结构实现方法的集合
data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
eface包含一个类型信息,可以为reflect提供帮助
type _type struct {
size uintptr //类型大小
ptrdata uintptr //前缀持有所有指针的内存大小
hash uint32 //数据hash值
tflag tflag
align uint8 //对齐
fieldalign uint8 //嵌入结构体时的对齐
kind uint8 //kind 有些枚举值kind等于0是无效的
alg *typeAlg //函数指针数组,类型实现的所有方法
gcdata *byte
str nameOff
ptrToThis typeOff
}
iface比eface 中间多了一层itab结构
type itab struct {
inter *interfacetype //接口类型
_type *_type //结构类型
link *itab
bad int32
inhash int32
fun [1]uintptr //可变大小 方法集合
}
itab 存储_type信息和[]fun方法集,从上面的结构我们就可得出,因为data指向了nil 并不代表interface 是nil,所以返回值并不为空,这里的fun(方法集)定义了接口的接收规则,在编译的过程中需要验证是否实现接口,接口的具体细节你可以阅读Go Data Structures: Interfaces
接下来是第二个例子:
package main
import (
"fmt"
)
type People interface {
Speak(string) string
}
type Stduent struct{}
func (stu *Stduent) Speak(think string) (talk string) {
if think == "bitch" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Stduent{}
think := "bitch"
fmt.Println(peo.Speak(think))
}
上面的代码是不能编译过去的,会提示没有实现该接口,只要我们把var peo People = Stduent{}修改为var peo People = &Stduent{}就可以了,为什么会有这种限制,
这是因为接口定义不规定实现者是否应该使用指针接收还是值接收实现接口。当使用接口时,不能保证底层类型是值还是指针。我们上面的例子中,我们定义了指针接受方法,修改为值接受方法:
func (stu Stduent) Speak(think string) (talk string) {
if think == "bitch" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
我们再次运行打印:
- You are a good boy
通过上面测试我们得出一个结论使用值传递方法,接口赋值使用var peo People = Stduent{}或者var peo People = &Stduent{},如果使用指针作为参数传递,则只能使用var peo People = &Stduent{},正是由于interface的灵活性,可以使用golang实现多态的特性,所以我们更要对interface多做深入了解。(本文未来可能会做一些细微的调整)
- 以太坊智能合约开发入门
- CatBoost:一个自动处理分类(CAT)数据的机器学习库
- Python机器学习的练习八:异常检测和推荐系统
- Blade 模板中有关 section 的那些事
- 分布式计划任务设计与实现
- 怎样在Python的深度学习库Keras中使用度量
- 网络设备配置管理与版本控制
- 使用Python对Instagram进行数据分析
- 解决多标签分类问题(包括案例研究)
- Docker Compose + GPU + TensorFlow = Heart
- Tensorflow生成模型收集: GANs与VAEs
- How to Install Nginx and PHP-FPM on FreeBSD 10
- 使用Apache MXNet分类交通标志图像
- CentOS 6.4 + nginx-1.2.5 + php-5.4.15 + MySQL-5.5.31
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 在Linux中使用tcpdump命令捕获与分析数据包详解
- easyswoole一键安装脚本及宝塔安装错误问题
- CentOS7系统增加swap的操作方法实例
- iOS逆向之OpenSSH登录iPhone
- linux中SUID,SGID与SBIT的奇妙用途详解
- 详解Linux文件操作知识点
- Linux中nohup与&的用法和区别详解
- Linux中有效地管理进程的8个命令
- Centos7 下安装python3及卸载的教程
- Linux使用VIM编辑器的方法
- Centos安装MYSQL8.X的教程
- Linux使用join -a1来合并两个文件
- ZFS是什么?使用ZFS的理由及特性介绍
- centos7.x 部署主、从DNS服务器问题
- Linux查看History记录加时间戳的小技巧