方法

方法

尽管没有被大众所接受的明确的OOP的定义, 从我们的理解来讲, 一个对象其实也就是一个简单的值或者一个变量, 这个对象中会包含一些方法, 而一个方法则是一个一个和特殊类型关联的函数. 一个面向对象的程序会使用方法来表达其属性和对应的操作, 这样使用这个对象的用户就不需要直接去操作对象, 而是借助方法来做这些事情.

方法声明

在函数声明时, 在其名字之前放上一个变量, 即是一个方法. 这个附加的参数会将该函数附加到这种类型上, 即相当于为这种类型定义了一个独占的方法.

函数名字前的变量称为方法的接受器, 早期的面向对象语言留下的遗产将调用一个方法称为”向一个对像发送消息”.

Go语言中, 我们并不会像其它语言那样用this或者self作为接收器; 我们可以任意选择接收器的名字. 由于接受器的名字会经常被使用到, 所以保持其在方法间传递时的一致性或简短性是不错的注意. 这里的建议是可以使用其类型的第一个字母的小写.

方法可以被声明到任意类型, 只要不是一个指针类型或者interface.

基于指针对象的方法

当接收器变量本身比较大或我们希望更新接受器对象, 我们可以用其指针而不是对象来声明方法.

在声明方法时, 如果一个类型名本身是一个指针的话, 是不允许其出现在接收器中的.

不管你的methodreceiver是指针类型还是非指针类型, 都是可以通过指针/非指针类型进行调用的, 编译器会帮你做类型转换.

在声明一个methodreceiver该是指针还是非指针类型时, 你需要考虑两方面的内部, 第一方面是这个对象本身是不是特别大, 如果声明为非指针变量时, 调用会产生一次拷贝; 第二方面是如果你用指针类型作为receiver, 那么你一定要注意, 这种指针类型指向的始终是一个块内存地址.

Nil也是一个合法接收器类型

就像一些函数允许nil指针作为参数一样, 方法理论上也可以用nil作为其接收器, 尤其当nil对于对象来说是合法的零值, 比如map或者slice.

当你定义一个允许nil作为接收器值的方法的类型时, 在类型前面的注释中指出nil变量代表的意义是很有必要的.

通过嵌入结构体来扩展类型

嵌套类型, 不是is a关系, 而是has a关系, 内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法.

在类型中内嵌的匿名字段也可能是一个命名类型的指针, 这种情况下字段和方法会被间接地引入到当前的类型(访问需要通过该指针指向的对象去取). 添加这一层间接关系让我们可以共享通用的结构并动态地改变对象间的关系.

方法只能在命名类型或指向类型的指针上定义, 但是多亏了内嵌, 有些时候我们给匿名struct类型来定义方法也有了手段.

方法值和方法表达式

我们经常选择一个方法, 并且在同一个表达式里执行, 比如常见的p.Distance()形式, 实际上将其分成两步来执行也是可能的. p.Distance叫作”选择器”, 选择器会返回一个方法”值”, 一个将方法Point.Distance绑定到特定接收器变量的函数. 这个函数可以不通过指定其接收器即可被调用; 即调用时不需要指定接收器, 只要传入函数的参数即可.

在一个包的API需要一个函数值, 且调用方希望操作的是某一个绑定了对象的方法的化, 方法”值”会非常实用.

T是一个类型时, 方法表达式可能会写作T.f或者(*T).f, 会返回一个函数”值”, 这种函数会将其第一个参数用作接收器, 所以可以用不写选择器的方式来进行调用.

实例: BIT数组

Go语言里的集合一般会用map[T]bool这种形式来表示, T代表元素类型.

一个bit数组通常会用一个无符号数或者称之为”字”的slice来表示, 每个元素的每一位都表示集合里的一个值. 当集合的第i位被设置时, 我们才说这个集合包含元素i.

封装

一个对象的变量或者方法如果对调用方是不可见的话, 一般就定义为”封装”. 封装有时候也叫作信息隐藏, 同时也是面向对象编程最关键的一个方面.

Go语言只有一种控制可见性的手段: 大写手写字母的标识符会从定义它们的包中被导出, 小写字母的则不会. 这种限制包内成员的方式同样适用于struct或者一个类型的方法. 因而如果我们想要封装一个对象, 我们必须将其定义为一个struct.

这种基于名字的手段使得语言中最小的封装单元是package, 而不是像其它语言一样的类型. 一个struct类型的字段对同一个包的所有代码都有可见性, 无论你的代码是写在一个函数还是一个方法里.

-------------本文结束感谢您的阅读-------------