Go 函数与方法(二)

93 阅读3分钟

方法

image.png

图片来源:static001.geekbang.org/resource/im…

  • 方法比函数多了一个receiver参数,这是go 实现组合的表现,也是方法与类型之间的联系。
  • Go的中方法必须归属reciever参数的类型,该方法就绑定到receiver的类型,类似Java 中 类的方法。
  • Go receiver 其实本质就是类似于java类中方法的this,this只的就是当前类的实例。

所以基于类与方法关系就方便理解以下约束了

  • receiver 参数类型不能和 方法列表和返回值列表冲突
  • 参数类型不能为指针类型和接口类型,必须是一个具体的类型。可以对比理解为java 的类 不能是一个“引用”,但*T 的基类是T不是 *T,T不是接口和指针就可以。

无论 receiver 参数的类型为 *T 还是 T,我们都把一般声明形式中的 T 叫做 receiver 参数 t 的基类型

  • 方法声明要和receiver的参数基类在同一个包中,可以对比理解为java 的方法是具体在一个class 中的,方法与类绑定。
  • 每个方法只能有一个receiver参数,方法只能直接属于一类。

调用方式


type sc struct{}

func (t T) f(n int) {
}

func main() {
    var t T
    t.f() // 通过类型T的变量实例调用方法

    p := &T{}
    p.f() // 通过类型*T的变量实例调用方法
    
    
}

go编译器会为方法调用时自动 解/取引用

本质上是Method expression

类型.func(receiver 参数)


var t T
t.Get()
(&t).Set(1)

等价于:

var t T
T.Get(t)
(*T).Set(&t, 1)

receiver 参数类型选择

  • 根据是否需要通过 receiver参数改变 参数实例,若需要则用 *T,反之用T
  • receiver参数非指针类型时,可能数据size较大,所以使用指针减少传递的开销。
  • (最重要的依据)是否需要实现接口,有可能T类型的方法集合没有接口方法集合,所以没有实现接口,而T 的方法集合包括了T本身的方法和T类型的方法,所以更有可能实现接口,从而判断选T类型还是*T类型作为receiver参数

类型嵌入(组合,类比 JAVA的继承)

独立自定义类型的方法都是显示实现,也就是T类型中一定有对应的方法声明和实现。同时也有不是显示实现的方法,也就是通过“组合”的方式拥有其他类型实现的方法。

勿把receiver参数不能是其他包的参数混淆。

接口类型嵌入

结构体类型嵌入

只用用与字段类型名相同的字段,即代表类型也代表字段名。 这种结构体字段声明的方式叫做结构体类型的嵌入,也叫作嵌入字段。但如果用 T T ,则不叫嵌入字段,无法实现“组合”

类型嵌入和方法集合

  • 结构体的方法集合中会包含嵌入接口字段的方法集合。
  • 在接口类型嵌入时 接口类型的方法集合有交集 在1.14后就不报错了,但是在结构体类型嵌入时,如果方法集合有交集可能会报错(如果结构体自身有该同名方法就不会报错)

解决办法:1、T自己实现交集的方法,在选择方法时就不会无法确定,因为会先使用自己的方法。2、将有交集的两个嵌入类型中的重复方法去重。