1. 接口值内部布局

  如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户定义的类型的值就可以赋给这个接口类型的值。这个赋值会把用户定义的类型的值存入接口类型的值。赋值完成后得到的值称为接口值。接口值是一个两个字长度的数据结构,第一个字包含一个指向内部表的指针。这个内部表叫作iTable,包含了所存储的值的类型信息和与这个值相关联的一组方法(也就是方法集)。第二个字是一个指向所存储值的指针。下图展示实体值赋值后接口值的内部布局:

        

                    实体值赋值后接口值的内部布局图

如果是一个指针赋值给接口的情况,类型信息会存储一个指向保存的类型的指针,而接口值第二个字依旧保存指向实体值的指针。如下图:

        

                    实体指针赋值后接口值的内部布局图

2. 方法集

  方法集定义了接口的接受规则。方法集中定义了一组关联到给定类型的值或者指针的方法。定义方法时使用的接收者的类型决定了这个方法是关联到值,还是关联到指针,还是两个都关联。先看下Go语言规范里定义的方法集的规则:

Values Methods Receivers
T (t T)
*T (t T) and (t *T)

  Go语言对该规范的描述中说道,T类型的值的方法集只包含值接收者声明的方法(因为有些值的地址获取不到),而指向T类型的指针的方法集既包含值接收者声明的方法,也包含指针接收者声明的方法。听起来会有些绕,难以理解。如果我们从参数传递的角度是理解这句话获取更好理解:T类型的值只能传递给值接收者声明的方法,指向T类型的指针既能传递给值接收者声明的方法,也能传递给指针接收者声明的方法。

  如果从接收者类型的视角来看方法集,就会比较好理解:

Methods Receivers Values
(t T) T and *T
(t *T) *T

  从接收者的视角来看的话,值接收者声明的方法能接受值类型和指针类型的参数,指针接收者声明的方法只能接受指针类型的参数。

3. 代码示例

3.1 如果接收者是值类型,用值调用和用指向值的指针调用都没有问题

package main

import (
"fmt"
) type notifier interface {
notify()
} type user struct {
name string
email string
} func (u user) notify() {
fmt.Printf("sending user email to %s<%s>\n", u.name, u.email)
} func sendNotification(n notifier) {
n.notify()
} func main() {
u := user{name: "lisa",
email: "test@qq.com",
} sendNotification(u)
sendNotification(&u)
}

3.2 如果接收者是指针类型,只能用指向值的指针来调用

package main

import (
"fmt"
) type notifier interface {
notify()
} type user struct {
name string
email string
} func (u *user) notify() {
fmt.Printf("sending user email to %s<%s>\n", u.name, u.email)
} func sendNotification(n notifier) {
n.notify()
} func main() {
u := user{name: "lisa",
email: "test@qq.com",
} // 这行代码会报如下错误:
// .\learn.go:29:18: cannot use u (type user) as type notifier in argument to sendNotification:
// user does not implement notifier (notify method has pointer receiver)
// sendNotification(u) sendNotification(&u)
}

4. 参考书籍:

  《Go语言实战》

最新文章

  1. 【11-23】mysql学习笔记02
  2. mongDB-- 3. 查询操作
  3. jQuery判断网页中的id是否有重复的
  4. Redis使用总结
  5. ASM:《X86汇编语言-从实模式到保护模式》第14章:保护模式下的特权保护和任务概述
  6. JavaWeb学习之JSTL自定义标签库的使用、JSTL自定义函数库(7)
  7. Selected SVN connector library is not available or cannot be loaded
  8. java volatile 和Transient 关键字
  9. java中创建对象 类名 对象名=new 类名(); 后面的()什么意思
  10. 超好用文件对比工具 – Beyond Compare
  11. VS Code调试.NET Core
  12. ssl通关的概念(一个)
  13. HDU 4283 You are the one(间隔DP)
  14. ASP.NET MVC应用程序使用axd格式文件
  15. 深入理解 JavaScript 异步系列(4)—— Generator
  16. registry key &#39;Java Runtime Environment&#39; has value&#39;1.8&#39;,but &#39;1.7&#39; is requaired(转)
  17. 201521123087《Java程序设计》第12周学习总结
  18. 详解Android数据存储技术
  19. SSM-1第一章 认识SSM框架和Redis
  20. VMware网络连接模式—桥接、NAT以及仅主机模式的详细介绍和区别

热门文章

  1. Salesforce 开发 | Salesforce与微信集成实操指南
  2. Unity 随机地图房间通道生成
  3. TCP的分分合合(面试必问)
  4. Xshell远程连接Linux系统
  5. D - A Game with Traps-- codeforces 1260D A
  6. HTML学习过程-(1)
  7. ADO.NET(一)
  8. form表单里的button调用js函数
  9. 关于“xxx”object is not callable的异常
  10. vue显示富文本