Go 语言方法

基本概念

Go 语言中方法(Methods)是定义在特定类型上的函数,类似于其他语言中类的方法,通过方法将函数与一个接收者(receiver)相关联,从而实现面向对象编程特性。

方法声明

方法在声明函数基础上多一个接收者参数:

func (receiver ReceiverType) MethodName(paramsList) ReturnType {
    // 方法体
}
  • receiver:接收者命名,通常采用接收者类型的第一个小写字母。一个方法只能有一个接收者,方法可以访问和修改接收者状态。
  • ReceiverType:接收者可以是除接口外任意类型。
  • MethodName:方法名。不同接收者可以具有同名方法。
  • paramsList:方法参数列表,可以没有参数。
  • ReturnType:方法返回类型,可以没有返回。

任何方法实际上都可以通过调整签名转为等价函数,即转移接收者作为其第一个参数:

package main

import "fmt"

// Rectangle 定义一个长方形结构体类型
type Rectangle struct {
	width, height int
}

// Area 方法作用在 Rectangle 类型接收者上
func (r Rectangle) Area() int {
	return r.width * r.height
}

// Area 函数和 Rectangle 类型上的 Area 方法等价
func Area(r Rectangle) int {
	return r.width * r.height
}

func main() {
	rect := Rectangle{width: 10, height: 5}

	fmt.Println("方法调用: ", rect.Area())
	fmt.Println("函数调用: ", Area(rect))
}

等价函数与类型之间虽然相互依赖,但没有直接绑定关系。方法绑定接收者后,能自动处理接收者指针与接收者值,调用方式保持一致:

package main

import "fmt"

type Rectangle struct{ width, height int }

// 方法绑定值接收者
func (r Rectangle) Area() int { return r.width * r.height }

// 方法绑定指针接收者
func (r *Rectangle) SetWidth(w int) { r.width = w }

func main() {
	// 值接收方法调用
	r := Rectangle{width: 10, height: 1}
	rp := &Rectangle{width: 100, height: 1}
	fmt.Println(r.Area(), rp.Area())

	// 指针接收方法调用
	r.SetWidth(20)
	rp.SetWidth(200)
	fmt.Println(r, rp)
}

方法继承

当使用嵌套结构体时,嵌入类型的方法可以直接被外层结构体调用。嵌入类型为匿名字段时,可以忽略字段名来调用内部类型方法,否则必须带上字段名:

package main

import "fmt"

// Shape 基本形状类型
type Shape struct {
	ShapeType string
}

// Type 方法返回形状类型
func (s Shape) Type() string {
	return s.ShapeType
}

// Circle 圆形,使用匿名字段继承 Shape
type Circle struct {
	Shape
	Radius float64
}

// Rectangle 矩形,使用具名字段继承 Shape
type Rectangle struct {
	Shape  Shape
	Length float64
	Width  float64
}

func main() {
	// 匿名字段的方法继承
	circle := Circle{Shape{"Circle"}, 5}
	fmt.Println(circle.Type())       // 直接调用内嵌类型的方法
	fmt.Println(circle.Shape.Type()) // 也可以通过字段名调用

	// 具名字段的方法继承
	rectangle := Rectangle{Shape{"Rectangle"}, 4, 2}
	fmt.Println(rectangle.Shape.Type()) // 调用时必须通过指定字段名
}

如果外部类型定义了与内部类型同名的方法,必须带上字段名来区分调用:

package main

import "fmt"

type Shape struct{ ShapeType string }

func (s Shape) Type() string { return "Shape: " + s.ShapeType }

// Circle 圆形,使用匿名字段继承 Shape
type Circle struct {
	Shape
	Radius float64
}

// Circle 拥有和内嵌匿名字段同名方法
func (c Circle) Type() string {
	return "Circle with radius: " + fmt.Sprint(c.Radius)
}

func main() {
	circle := Circle{Shape{"Circle"}, 5}
	fmt.Println(circle.Type())       // 会调用 Circle 的 Type 方法
	fmt.Println(circle.Shape.Type()) // 调用 Shape 的 Type 方法
}