方法就是函数,但它是属于某一个类型。
方法的语法为
你可以给任意类型定义方法
方法实际上是函数,上述方法等价于下列函数
方法其实就是把函数参数移动到前面作为函数的接受者。
方法的定义必须要与类型的定义在同一个包里,例如你不能给int
类型创建方法,因为int
类型的定义不在main
包中:
但你可以给int
起一个别名,给别名定义方法, 例如上一个例子。
定义一个counter类型,它有一个值接收者的递增方法(increment):
输出
为什么, 是0,而不是1呢?我们说过,方法其实就是函数
等价于
Go中一切传递都是进行复制,索引increment
函数中的c只是复制了传入的0
,和main
函数的c
没有关系。
我们把increment方法改为指针接收者,结果就会大不一样:
输出
为什么呢?
指针接受者的方法
等价于
传入的是指针地址,复制的是指针地址,main
函数中的c
的地址存的值发送了改变,所以打印1。
这就是指针接受者。当我们想改变c
时,使用指针介绍者。或者当c
内存占用很大时,我们使用指针接受者,减少函数传递时复制的开销,因为指针占用的内存永远是8个字节。
不知道你注意没有,counter
调用指针接受者是这样的:
c 是counter
类型,而不是*counter
类型,为什么可以直接调用increment
方法呢?
这是Go提供的指针语法糖, Go会自动帮我们取地址:
等价于
这能简化我们的操作。
同样,Go也会自动地解引用,考虑下列值接受者
c是指针类型(*counter)的变量,increment
是值接受者。
等价于
Go帮我们自动解引用。
聪明的你,上面的代码输出0还是1?
运行,输出
为什么呢?因为increment
传入的是值类型counter
,而不是指针类型(*counter)。
结构体(类型)就像一个人, 他有姓名,年龄等属性(字段), 也会唱,跳和rap等技能(方法)。
运行,输出
如何更好地复用代码呢?其他语言通过面向对象来实现,Go特立独行,使用组合来实现类似面向对象的功能。
以上面的person
结构体为例,如果我想在person
的基础上创建新的结构体smartPerson
,如何做呢?这时可以使用嵌套:
嵌套结构体,这时不用使用字段名,直接把类型写入即可。smartPerson
自己有一个iQ
字段
新的结构体可以访问person的字段和方法
smartPerson
也可以实现和person
相同的方法
这时调用sing()
结果是哪个呢?
输出
是自身的sing
方法。通过结构体的嵌套,我们可以“重写”被嵌入的结构体的方法。
如果想调用被嵌套的sing
方法,可以使用
指明字段
运行,输出:
好的。我们实现了类似“继承”的功能,但它是组合。组合优于继承!
完整代码
输出