mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-19 17:32:02 +08:00
format
This commit is contained in:
@@ -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)中同样的问题
|
||||
|
||||
## 链接
|
||||
|
@@ -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 高级编程
|
||||
|
Reference in New Issue
Block a user