在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将函数附加这种类型上,即相当于这这种类型定义了一个独占的方法。
/* 这个附加的参数p,叫做方法的接收器(receiver),早期的面向对象语言留下的遗产将调用一个方法称为"向一个对象发送消息"
在Go语言中,我们并不会像其它语言那样用this或者self作为接收器;我们可以任意的选择接收器的名字。由于接收器的名字经常会被使用到,所以保持其在方法间传递时的一致性和简短性是不错的主意。建议可以使用其类型的第一个字母,比如这里使用了Point的首字母p*/
type Point struct{ X, Y float64 }
func (p Point) Distance(q Point) float64 {
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
每种类型都有其方法的命名空间,我们在用Distance这个名字的时候,不同的Distance调用指向了不同类型里的Distance方法。
基于指针对象的方法 当调用一个函数时,会对其每一个参数值进行拷贝,如果一个函数需要更新一个变量,或者函数的其中一个参数实在太大我们希望能够进行这种默认的拷贝,这种情况我们可以使用指针。
// 这个方法的名字是(*Point).ScaleBy
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
p := Point{1, 2}
pptr := &p
pptr.Distance(q)
(*pptr).Distance(q)
// 编译器在这里也会给我们隐式地插入*这个操作符,所以上面这两种写法等价
- 不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会帮你做类型转换。
- 在声明一个method的receiver该是指针还是非指针类型时,你需要考虑两方面的内部,第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;第二方面是如果你用指针类型作为receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝。
通过嵌入结构体来扩展类型
package main
import (
"fmt"
"image/color"
)
type Point struct{X, Y float64}
type ColoredPoint struct {
Point
Color color.RGBA
}
func (p Point)Distance() float64{
return p.X
}
func main() {
red := color.RGBA{255, 0, 0, 255}
var p = ColoredPoint{Point{1, 1}, red}
fmt.Println(p.Distance())
}
/*
p这里可以直接调用p.Distance()方法。这里可以将Point看作一个基类,ColoredPoint是其子类或继承类。
*/
封装 一个对象的变量或者方法如果对调用方不可见的话,一般就被定义为”封闭”。封装有时候也被叫信息隐藏,同时也是面向对象编程最关键的一个方面。 Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。这种限制包内成员的方式同样适用于struct或者一个类型的方法。因而如果我们想要封装一个对象,我们必须将其定义为一个struct。
Ref: 1.The Go Programming Language