func
关键字之后的括号中指定 receiver。recv
是 receiver 的实例,Method1 是它的方法名,那么方法调用遵循传统的 object.name
选择器符号:recv.Method1()。recv
是一个指针,Go 会自动解引用。recv
的值,可以用 _ 替换它,比如:recv
就像是面向对象语言中的 this
或 self
,但是 Go 中并没有这两个关键字。随个人喜好,你可以使用 this
或 self
作为 receiver 的名字。下面是一个结构体上的简单方法的例子:employee
,它有一个 salary
字段,给这个结构体定义一个方法 giveRaise
来按照指定的百分比增加薪水。time.Time
上定义如下方法:method recv.Method1 is not an expression, must be called
recv
最常见的是一个指向 receiver_type 的指针(因为我们不想要一个实例的拷贝,如果按值调用的话就会是这样),特别是在 receiver 类型是结构体时,就更是如此了。pointer_value.go
作了说明:change()
接受一个指向 B 的指针,并改变它内部的成员;write()
通过拷贝接受 B 的值并只输出B的内容。注意 Go 为我们做了探测工作,我们自己并没有指出是否在指针上调用方法,Go 替我们做了这些事情。b1 是值而 b2 是指针,方法都支持运行了。write()
中改变接收者b的值:将会看到它可以正常编译,但是开始的 b 没有被改变。Point3
的值来做计算:Point3
是作为值传递给方法的,因此传递的是它的拷贝,这在 Go 中是合法的。也可以在指向这个类型的指针上调用此方法(会自动解引用)。p3
定义为一个指针:p3 := &Point{ 3, 4, 5}
。p3.Abs()
来替代 (*p3).Abs()
。*TwoInts
的方法 AddThem()
,它能在类型 TwoInts
的值上被调用,这是自动间接发生的。two2.AddThem
可以替代 (&two2).AddThem()
。Meth()
,并且 t
是这个类型的变量,那么 t.Meth()
会被自动转换为 (&t).Meth()
。List
在值上有一个方法 Len()
,在指针上有一个方法 Append()
,但是可以看到两个方法都可以在两种类型的变量上被调用。person2.go
中的 person
包:类型 Person
被明确的导出了,但是它的字段没有被导出。例如在 use_person2.go
中 p.firstName
就是错误的。该如何在另一个程序中修改或者只是读取一个 Person
的名字呢?sync
(参考第 9.3 节)中的方法。在第 14.17 节中我们会通过 goroutines 和 channels 探索另一种方式。Engine
接口类型,一个 Car
结构体类型,它包含一个 Engine
类型的匿名字段:method3.go
的完整例子,它展示了内嵌结构体上的方法可以直接在外层类型的实例上调用:fmt.Println(n.Abs())
会打印 500
。type Child struct { Father; Mother}
。在第 10.6.7 节中会进一步讨论这个问题。Car
和 Engine
可运行的例子,并且给 Car
类型一个 wheelCount
字段和一个 numberOfWheels()
方法。Mercedes
类型,它内嵌 Car
,并新建 Mercedes
的一个实例,然后调用它的方法。Mercedes
类型上创建方法 sayHiToMerkel()
并调用它。Customer
类型,我们想让它通过 Log
类型来包含日志功能,Log
类型只是简单地包含一个累积的消息(当然它可以是复杂的)。如果想让特定类型都具备日志功能,你可以实现一个这样的 Log
类型,然后将它作为特定类型的一个字段,并提供 Log()
,它返回这个日志的引用。String()
功能):Customer
也不需要 Add
方法,它使用 Log
的 Add
方法,Customer
有自己的 String
方法,并且在它里面调用了 Log
的 String
方法。CameraPhone
,通过它可以 Call()
,也可以 TakeAPicture()
,但是第一个方法属于类型 Phone
,第二个方法属于类型 Camera
。point.go
开始(第 10.1 节的练习):使用方法来实现 Abs()
和 Scale()
函数,Point
作为方法的接收者类型。也为 Point3
和 Polar
实现 Abs()
方法。完成了 point.go
中同样的事情,只是这次通过方法。Base
,它包含一个字段 id
,方法 Id()
返回 id
,方法 SetId()
修改 id
。结构体类型 Person
包含 Base
,及 FirstName
和 LastName
字段。结构体类型 Employee
包含一个 Person
和 salary
字段。employee
实例,然后显示它的 id
。Open()
、Read()
、Write()
等。想写规范的 Go 程序,就应该遵守这些约定,给方法合适的名字和签名,就像那些通用方法那样。这样做会使 Go 开发的软件更加具有一致性和可读性。比如:如果需要一个 convert-to-string 方法,应该命名为 String()
,而不是 ToString()
(参考第 10.7 节)。Integer
类型,并添加一些类似转换成字符串的方法,在 Go 中可以如下定义:Integer
的定义放在一起,在 Ruby 中可以直接在基本类型 int 上定义这个方法。goop
包(Go Object-Oriented Programming),它由 Scott Pakin 编写: 它给 Go 提供了 JavaScript 风格的对象(基于原型的对象),并且支持多重继承和类型独立分派,通过它可以实现你喜欢的其他编程语言里的一些结构。variable.method()
,在使用 Go 以前,在哪儿碰到过面向对象的点号?type Integer int
,完成 get()
方法的方法体: func (p Integer) get() int { ... }
。func f(i int) {}; var v Integer
,如何就 v 作为参数调用f?Integer
定义为 type Integer struct {n int}
,完成 get()
方法的方法体:func (p Integer) get() int { ... }
。Integer
,和 b)中同样的问题。