什么是方法
方法其实就是一个函数,在func
这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的。
创建一个方法的基本语法如下所示,创建了一个接收器类型为Type的方法methodName。
func (t Type) methodName(parameter list) {
}
为什么已经有函数了还需要方法?
- Go不是纯粹的面向对象编程语言,而且Go不支持类。因此,基于类型的方法是一种实现和类相似行为的途径。
- 相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。
指针接收器与值接收器
值接收器和指针接收器之间的区别在于,在指针接收器的方法内部的改变对于调用者是可见的。
什么时候使用指针接收器,什么时候使用值接收器?
- 一般来说,指针接收器可以用在:对方法内部的接收器所做的改变应该对调用者可见时。
- 指针接收器也可以被使用在如下场景:当拷贝一个结构体的代价过于昂贵时。考虑下一个结构体有很多字段,在方法内使用这个结构体作为值接收器需要拷贝整个结构体,这是很昂贵的。在这种情况下使用指针接收器。结构体不会被拷贝,只会传递一个指针到方法内部使用。
- 在其它的所有情况,值接收器都可以被使用。
匿名字段的方法
属于结构体的匿名字段的方法可以被直接使用,就好像这些方法是属于定义了匿名字段的结构体一样。
package main
import "fmt"
type address struct {
city string
state string
}
func (a address) fullAddress() {
fmt.Printf("Full address: %s, %s\n", a.city, a.state)
}
type person struct {
firstName string
lastName string
address
}
func main() {
p := person{
firstName: "Elon",
lastName: "Musk",
address: address{
city: "Los Angeles",
state: "California",
},
}
p.fullAddress( ) // 访问 address 结构体的 fullAddress 方法
}
在方法中使用值接收器与在函数中使用值参数
- 当一个函数有一个值参数,它只能接受一个值参数。
- 当一个方法有一个值接收器,它可以接受值接收器和指针接收器。
package main
import "fmt"
type rectangle struct {
length int
width int
}
func area(r rectangle) {
fmt.Printf("Area Function result: %d\n", r.length*r.width)
}
func (r rectangle) area() {
fmt.Printf("Area Method result: %d\n", r.length*r.width)
}
func main() {
r := rectangle{
length: 10,
width: 5,
}
area(r)
r.area()
p := &r
// 使用指针接收器 p 调用了只接受一个值接收器的方法 area,这是完全有效的。原因是
// 当 area 有一个值接收器时,Go语言会自动把 p.area() 解释为 (*p).area()
p.area()
}
在方法中使用指针接收器与在函数中使用指针参数
- 当一个函数有一个指针参数,它只能接受一个指针参数。
- 当一个方法有一个指针接收器,它可以接受值接收器和指针接收器。
package main
import "fmt"
type rectangle struct {
length int
width int
}
func perimeter(r *rectangle) {
fmt.Println("perimeter function output:", 2*(r.length+r.width))
}
func (r *rectangle) perimeter() {
fmt.Println("perimeter method output:", 2*(r.length+r.width))
}
func main() {
r := rectangle{
length: 10,
width: 5,
}
p := &r
perimeter(p)
p.perimeter()
// 通过值接收器 r 来调用有指针接收器的方法 perimeter,这是被允许的。
// Go会自动把 r.perimeter() 解释为 (&r).perimeter。
r.perimeter()
}
在非结构体上的方法
Go同样支持在非结构体类型上定义方法,但需要注意一个前提:为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中。
举个例子,如果尝试把一个add
方法添加到内置的类型int
,这是不允许的,因为add
方法的定义和int
类型的定义不在同一个包中。一个变通的方法是:为内置类型int创建一个类型别名,然后创建一个以该类型别名为接收器的方法。
package main
import "fmt"
type myInt int
func (a myInt) add(b myInt) myInt {
return a+b
}
func main() {
num1 := myInt(5)
num2 := myInt(10)
sum := num1.add(num2)
fmt.Println("Sum is", sum) // Sum is 15
}
reference: