入门11_方法接口和嵌入类型

方法在编译时静态绑定,依托于具体的类型
接口对应的方法是在运行时动态绑定
进程内初始化顺序 初始化导入包的常量和变量(可以导出的变量)—>包的init函数,不同文件内顺序是未定义的, 同一文件调用顺序是定义顺序—>main包内的常量变量和init-—>main函数

方法

在 Go 语言中,结构体就像是类的一种简化形式,那么面向对象程序员可能会问:类的方法在哪里呢?在 Go 中有一个概念,它和方法有着同样的名字,并且大体上意思相同:Go 方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量。因此方法是一种特殊类型的函数。
接收者类型可以是(几乎)任何类型,不仅仅是结构体类型:任何类型都可以有方法,甚至可以是函数类型,可以是 int、bool、string 或数组的别名类型。但是接收者不能是一个接口类型,因为接口是一个抽象定义,但是方法却是具体实现;如果这样做会引发一个编译错误:invalid receiver type…。
最后接收者不能是一个指针类型,但是它可以是任何其他允许类型的指针
一个类型加上它的方法等价于面向对象中的一个类。一个重要的区别是:
在 Go 中,类型的代码和绑定在它上面的方法的代码可以不放置在一起,它们可以存在在不同的源文件,唯一的要求是:它们必须是同一个包的
类型 T(或 *T)上的所有方法的集合叫做类型 T(或 *T)的方法集(method set)。
因为方法是函数,所以同样的,不允许方法重载,即对于一个类型只能有一个给定名称的方法。但是如果基于接收者类型,是有重载的:具有同样名字的方法可以在 2 个或多个不同的接收者类型上存在

接口

Go 语言不是一种 “传统” 的面向对象编程语言:它里面没有类和继承的概念
接口是golang中实现多态性的好途径。接口类型是对其他类型行为的概括与抽象,对于一个具体的类型,无需声明它实现了哪些接口,只提供接口所必须的方法即可。
类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。
实现某个接口的类型(除了实现接口方法外)可以有其他的方法。一个类型可以实现多个接口。
接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。
即使接口在类型之后才定义,二者处于不同的包中,被单独编译:只要类型实现了接口中的方法,它就实现了此接口

调用者,接收者,方法集

调用者:
使用指针调用和使用值调用,操作是相同的,go对其做了隐式操作。也就是说,不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。
接收者:
如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用者;
如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。

结论:
实现了接收者是值类型的接口方法,相当于自动实现了接收者是指针类型的方法;
实现了接收者是指针类型的接口方法,不会自动生成对应接收者是值类型的方法。

类型 *T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
类型 T 的可调用方法集包含接受者为 T 的所有方法
类型 T 的可调用方法集不包含接受者为 *T 的方法

Golang方法集 :每个类型都有与之关联的方法集,这会影响到接口实现规则。

1
2
3
4
5
? 类型 T 方法集包含全部 receiver T 方法。
? 类型 *T 方法集包含全部 receiver T + *T 方法。
? 如类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法。
? 如类型 S 包含匿名字段 *T,则 S 和 *S 方法集包含 T + *T 方法。
? 不管嵌入 T 或 *T,*S 方法集总是包含 T + *T 方法。

函数与方法的区别

1、对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然。
2、对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。
方法绑定的限制
01,非内置类型(但可别名后绑定)
02,非接口

嵌入类型

结构体类型可以包含匿名或者嵌入字段。也叫做嵌入一个类型。当我们嵌入一个类型到结构体中时,该类型的名字充当了嵌入字段的字段名
当我们嵌入一个类型,这个类型的方法就变成了外部类型的方法,但是当它被调用时,方法的接受者是内部类型(嵌入类型),而非外部类型。— Effective Go
Go 语言中内部类型方法集提升的规则:
给定一个结构体类型 S 和一个命名为 T 的类型,方法提升像下面规定的这样被包含在结构体方法集中:
如果 S 包含一个匿名字段 T,S 和 S 的方法集都包含接受者为 T 的方法提升。
这条规则说的是当我们嵌入一个类型,嵌入类型的接受者为值类型的方法将被提升,可以被外部类型的值和指针调用。
对于 \
S 类型的方法集包含接受者为 *T 的方法提升
这条规则说的是当我们嵌入一个类型,可以被外部类型的指针调用的方法集只有嵌入类型的接受者为指针类型的方法集,也就是说,当外部类型使用指针调用内部类型的方法时,只有接受者为指针类型的内部类型方法集将被提升。
如果 S 包含一个匿名字段 *T,S 和 *S 的方法集都包含接受者为 T 或者 *T 的方法提升
这条规则说的是当我们嵌入一个类型的指针,嵌入类型的接受者为值类型或指针类型的方法将被提升,可以被外部类型的值或者指针调用。

编译器会因为我们同时有两个接口实现而报错吗
不会,因为当我们使用嵌入类型时,类型名充当了字段名。嵌入类型作为结构体的内部类型包含了自己的字段和方法,且具有唯一的名字。所以我们可以有同一接口的内部实现和外部实现。
如果编译器接受这样的定义,那么当接口调用时编译器要怎么确定该使用哪个实现
如果外部类型包含了符合要求的接口实现,它将会被使用。否则,通过方法提升,任何内部类型的接口实现可以直接被外部类型使用。

参考

go 函数 方法 接口:https://www.cnblogs.com/sfth/p/10744654.html
Go 语言中的方法,接口和嵌入类型:https://studygolang.com/articles/2935
go语言结构体定义方法时,值作为接收者和指针作为接收者的区别:https://blog.csdn.net/u014239709/article/details/89552933
Go struct(结构体)值接收者和指针接收者的区别:https://blog.csdn.net/QiuHaoqian/article/details/107861222
Go语言之Go 语言方法:https://www.cnblogs.com/heych/p/12579558.html
go语言学习:方法:https://blog.csdn.net/weixin_44202489/article/details/105537011

go入门系列
入门02_IDE安装
入门03_工具链
入门04_入门demo和基本类型
入门05_go升级版本
入门06_教程biancheng
入门08_教程编程时光
入门09_Go语言高级编程
入门10_包导入
入门11_方法接口和嵌入类型
入门12_数组和切片

#
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×