This commit is contained in:
leisore
2015-08-12 11:50:20 +08:00
parent 0d5fb59871
commit b474d92cd8
2 changed files with 26 additions and 22 deletions

View File

@@ -2,17 +2,17 @@
## 10.6.1 方法是什么
在Go中结构体就像是类的一种简化形式那么OO程序员可能会问类的方法在哪里呢在Go中有一个概念它和方法有着同样的名字并且大体上意思相同Go方法是作用在接receiver上的一个函数者是某种类型的变量。因此方法是一种特殊类型的函数。
在Go中结构体就像是类的一种简化形式那么OO程序员可能会问类的方法在哪里呢在Go中有一个概念它和方法有着同样的名字并且大体上意思相同Go方法是作用在接receiver上的一个函数者是某种类型的变量。因此方法是一种特殊类型的函数。
者类型可以是几乎任何类型不仅仅是结构体类型任何类型都可以有方法甚至可以是函数类型可以是int、bool、string或数组的alias类型。但是接者不能是一个接口类型(参考 第11章因为接口是一个抽象定义但是方法却是具体实现如果这样做会引发一个编译错误*invalid receiver type…*
者类型可以是几乎任何类型不仅仅是结构体类型任何类型都可以有方法甚至可以是函数类型可以是int、bool、string或数组的alias类型。但是接者不能是一个接口类型(参考 第11章因为接口是一个抽象定义但是方法却是具体实现如果这样做会引发一个编译错误*invalid receiver type…*
最后接者不能是一个指针类型,但是它可以是任何其他允许类型的指针。
最后接者不能是一个指针类型,但是它可以是任何其他允许类型的指针。
一个类型加上它的方法等价于OO中的一个类。一个重要的区别是在Go中类型的代码和绑定在它上面的方法的代码可以不放置在一起它们可以存在在不同的源文件唯一的要求是它们必须是同一个包的。
类型T或*T上的所有方法的集合叫做类型T或*T的方法集。
因为方法是函数,所以同样的,不允许方法重载,即对于一个类型只能有一个给定名称的方法。但是如果基于接者类型是有重载的具有同样名字的方法可以在2个或多个不同的接者类型上存在,比如在同一个包里这么做是允许的:
因为方法是函数,所以同样的,不允许方法重载,即对于一个类型只能有一个给定名称的方法。但是如果基于接者类型是有重载的具有同样名字的方法可以在2个或多个不同的接者类型上存在,比如在同一个包里这么做是允许的:
```go
func (a *denseMatrix) Add(b Matrix) Matrix
@@ -175,23 +175,23 @@ First 3 chars: Mon
方法在变量上被调用:*recv.Method1()*
在接者是指针时,方法可以改变接者的值(或状态),这点函数也可以做到(当参数作为指针传递,即通过引用调用时,函数也可以改变参数的状态)。
在接者是指针时,方法可以改变接者的值(或状态),这点函数也可以做到(当参数作为指针传递,即通过引用调用时,函数也可以改变参数的状态)。
!!不要忘记Method1后边的括号(),否则会引发编译器错误:*method recv.Method1 is not an expression, must be called *!!
者必须有一个显式的名字,这个名字必须在方法中被使用。
者必须有一个显式的名字,这个名字必须在方法中被使用。
*receiver_type*叫做*(接者)基本类型*,这个类型必须在和方法同样的包中被声明。
*receiver_type*叫做*(接者)基本类型*,这个类型必须在和方法同样的包中被声明。
在Go中(接者)类型关联的方法不写在类型结构里面,就像类那样;耦合更加宽松;类型和方法之间的关联由接者来建立。
在Go中(接者)类型关联的方法不写在类型结构里面,就像类那样;耦合更加宽松;类型和方法之间的关联由接者来建立。
*方法没有和数据定义(结构体)混在一起:它们是正交的类型;表示(数据)和行为(方法)是独立的。*
## 10.6.2 指针或值作为接
## 10.6.3 指针或值作为接
鉴于性能的原因recv最常见的是一个指向receiver_type的指针因为我们不想要一个实例的拷贝如果按值调用的话就会是这样特别是在receiver类型是结构体时就更这样了。
如果想要方法改变接者的数据,就在接者的指针类型上定义该方法。否则,就在普通的值类型上定义方法。
如果想要方法改变接者的数据,就在接者的指针类型上定义该方法。否则,就在普通的值类型上定义方法。
下面的例子pointer_value.go作了说明change()接受一个指向B的指针并改变它内部的成员write()接受通过拷贝接受B的值并只输出B的内容。注意Go为我们做了探测工作我们自己并没有指出是是否在指针上调用方法Go替我们做了这些事情。b1是值而b2是指针方法都支持运行了。
@@ -228,9 +228,9 @@ func main() {
*/
```
试着在write()中改变接者b的值将会看到它可以正常编译但是开始的b没有被改变。
试着在write()中改变接者b的值将会看到它可以正常编译但是开始的b没有被改变。
我们知道方法不需要指针作为接如下面的例子我们只是需要Point3的值来做计算
我们知道方法不需要指针作为接如下面的例子我们只是需要Point3的值来做计算
```go
type Point3 struct { x, y, z float }
@@ -246,7 +246,7 @@ func (p Point3) Abs float {
可以这样写: * p3.Abs() 来替代 (*p3).Abs() *
像例子10.11(method1.go)中接者类型是*TwoInts的方法AddThem()它能在类型TwoInts的值上被调用这是自动间接发生的。
像例子10.11(method1.go)中接者类型是*TwoInts的方法AddThem()它能在类型TwoInts的值上被调用这是自动间接发生的。
因此two2.AddThem可以替代(&two2).AddThem()。
@@ -334,7 +334,7 @@ func main() {
对象的字段属性不应该由2个或2个以上的不同线程在同一时间去改变。如果在程序发生这种情况为了安全并发访问可以使用包sync(参考9.3)中的方法。在14.17我们会通过goroutines和channels探索另一种方式。
** 10.6.5 内嵌类型的方法和继承
## 10.6.5 内嵌类型的方法和继承
当一个匿名类型被内嵌在结构体中时,匿名类型的可见方法也同样被内嵌---在效果上等同于外层类型*继承*了这些方法:*将父类型放在子类型中来实现亚型*。这个机制提供了一种简单的方式来模拟经典OO语言中的子类和继承相关的效果也类似Ruby中的混入(mixin)。
@@ -416,7 +416,7 @@ func (n *NamedPoint) Abs() float64 {
然后仅在Mercedes类型上创建方法sayHiToMerkel()并调用它。
** 10.6.6 如何在类型中嵌入功能
## 10.6.6 如何在类型中嵌入功能
主要有两种方法来实现在类型中嵌入功能:
@@ -526,7 +526,7 @@ func (c *Customer) String() string {
因此一个好的策略是创建一些小的、可复用的类型作为一个工具箱,用于组成域类型。
** 10.6.7 多重继承
## 10.6.7 多重继承
多重继承指的是类型获得多个父类型行为的能力它在传统的面向对象语言中通常是不被实现的C++和Python例外。因为在类继承层次中多重继承会给编译器引入额外的复杂度。但是Go语言中通过在类型中嵌入所有必要的父类型可以很简单的实现多重继承。
@@ -618,11 +618,11 @@ func main() {
}
```
** 10.6.8 通用方法和方法命名
## 10.6.8 通用方法和方法命名
在编程中一些基本操作会一遍又一遍的出现,比如打开(Open)、关闭(Close)、读(Read)、写(Write)、排序(Sort)等等,并且它们都有一个大致的意思:打开(Open)可以作用于一个文件、一个网络连接、一个数据库连接等等。具体的实现可能千差万别但是基本的概念是一致的。在Go语言中通过使用接口参考 第11章标准库广泛的应用了这些规则在标准库中这些通用方法都有一致的名字比如Open()、Read()、Write()等。想写规范的Go程序就应该遵守这些约定给方法合适的名字和签名就像那些通用方法那样。这样做会使Go开发的软件更加具有一致性和可读性。比如如果需要一个convert-to-string方法应该命名为String()而不是ToString()(参考10.7).
** 10.6.9 和其他面向对象语言比较Go的类型和方法
## 10.6.9 和其他面向对象语言比较Go的类型和方法
在如C++、Java、C#和Ruby这样的面向对象语言中方法在类的上下文中被定义和继承在一个对象上调用方法时运行时会检测类以及它的超类中是否有此方法的定义如果没有会导致异常发生。
@@ -645,7 +645,7 @@ func (i *Integer) String() string {
在Java或C#中这个方法需要和类Integer的定义放在一起在Ruby中可以直接在基本类型int上定义这个方法。
*总结*
**总结:**
在Go中类型就是类数据和关联的方法。Go不知道类似OO语言的类继承的概念。继承有两个好处代码复用和多态。
@@ -653,18 +653,21 @@ func (i *Integer) String() string {
许多开发者说相比于类继承Go的接口提供了更强大、却更简单的多态行为。
*备注*
**备注**
如果真的需要更多OO的能力看一下goop包Go Object-Oriented Programming它来自与Scott Pakin[(https://github.com/losalamos/goop]: 它给Go提供了JavaScript风格的对象基于原型的对象并且支持多重继承和类型独立分派通过它可以实现你喜欢的其他编程语言里的一些结构。
** 问题 10.1
** 问题 10.1**
我们在某个类型的变量上使用点号调用一个方法variable.method()在使用Go以前在哪儿碰到过OO的点号
** 问题 10.2
** 问题 10.2**
a) 假设定义: `type Integer int`完成get()方法的方法体: `func (p Integer) get() int { ... }`
b) 定义: `func f(i int) {}; var v Integer` 如何就v作为参数调用f
c) 假设Integer定义为`type Integer struct {n int}`完成get()方法的方法体:`func (p Integer) get() int { ... }`
d) 对于新定义的Integer和b)中同样的问题
## 链接

View File

@@ -90,6 +90,7 @@
- 10.3 [使用自定义包中的结构体](10.3.md)
- 10.4 [带标签的结构体](10.4.md)
- 10.5 [匿名字段和内嵌结构体](10.5.md)
- 10.6 [方法](10.6.md)
- 第11章接口interface与反射reflection
## 第三部分Go 高级编程