Go语言的面向对象
- 对象是拥有方阿飞的值或变量
- 方法是一种特殊的函数
方法声明
与普通的函数声明相比,方法的声明需要在函数的名字前多加一个擦拭保护,表示将这个方法绑定在这个参数对应的类型上
func (p Point) Distance(q Point) float64 {
return math.Hypot(q.X-p.X,q.Y-p.Y)
}
- 附加的参数p称为方法的接收者,其作用和C++面向对象里的方法中的
this
是差不多意思 - 类型拥有的所有方法名都必须是唯一的,但是不同类型可以有相同的方法名】
指针接收者的方法
在C++中,对象的方法由于会自动传一个this的指针进去,所以可以在方法中很轻松的修改“接收者”本身的值。而在Go中,由于接收者也作为一个参数出现了,其自然也是按值传递,所以要想对接收者本身做出更改,则必须声明指针作为接收者的方法
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
为了方便调用,以*T
为接收者的方法可以接受T
变量的调用,其中会自动执行(&t).somfunc
;以T
为接收者的方法可以接受*T
变量的调用,其中会自动执行(*t).somfunc
;
nil
也是合法的接收者,这是因为对于类似map,slice
的类型,nil
是有意义的零值(空表)
通过结构体内嵌组成类型
这一块的内容有点类似C++中,派生类的引用或指针可以隐式的向基类的转换,从而调用到基类的方法。但是作者这里又强调了,必须将Go语言的方法与面向对象的方法区别开来,因为在这里不存在基类和派生类,存在的是内嵌(组合)
考虑内嵌一个Point
的ColoredPoint
类型
type ColoredPoint struct {
Point
Color color.RGBA
}
我们可以通过类型为ColoredPoint
的接收者调用内嵌类型Point
的方法,该方法实际上直接的操作到ColoredPoint
的Point
组件
var p ColoredPoint
p.ScaleBy(2)
实际上相当于自动生成了下面的代码
func (p *ColorPoint) ScaleBy(factor float64) {
p.Point.ScaleBy(factor)
}
当查找方法时,会执行一个树形的搜索:先搜索当前类型有没有该名字的方法,然后进入内嵌的类型搜索。若同一搜索层级出现同名的方法,则会报错
方法变量与表达式
应用我们之前讲到的,函数也是值知识,并注意到方法也是一种特殊的值
方法变量
将方法绑定到一个特定的接收者上,并赋予新名字,使得新定义的函数可以只提供实参,接收者沿用我们绑定的接收者
p := Point{1,2}
distanceFromP := p.Distance
d := distanceFromP(p) //0
方法表达式
将一个方法“重命名”成一个函数,原来的接收者成为新函数的第一个形参,原来的形参成为新函数从第二个开始的形参。
distance := Point.Distace
d := distance(p,q)
- 注意右侧要写成
T.f
或(*T).f
,其中T
是方法绑定到的类型名
封装
我们知道,Go语言中唯一与变量可见性有关的,就是“大写字母开头的标识符可以从包中导出”,这告诉我们
- 封装的单位是包
- 我们可以使用结构体来达到封装一个对象的效果
比如
type IntStack struct {
elements []int
}
- 这时,在包内,我们可以通过
s.elements
访问到内部的组件,编写相关方法 。但是在包外,我们只能接触到IntStack
,而接触不到其内部的实现细节。这其实就和private
差不多了 - 类似的,对于我们想要其成为“公有方法”和“公有部分”的标识符,我们使用大写;其它使用小写。同这一区别,我们就达到了我们熟悉的
public:,private:
效果