Golang method 方法详解
Posted Wallace JW
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang method 方法详解相关的知识,希望对你有一定的参考价值。
前言
作为一种面向对象编程语言,Golang 和其他的 OOP 语言在面向对象的实现上有较大的区别,他没有 class
的概念,而是通过 结构体(struct)-方法(method)-接口(interface)
的组合使用来实现面向对象的思想。在之前的文章 Golang 复合类型 中已经介绍过结构体,本文将介绍 method
的用法。
文章目录
基本语法
接收器
方法的使用和函数非常相似,都是通过包装一段代码块来实现某种可复用的功能,不同的是,函数的输入通过入参来定义,而对于方法,声明时需要在方法名之前声明一个带类型的参数作为方法的接收器(receiver)
,这个附加的参数会将该函数附加到这种类型上,从而成为一种专用于这种数据类型的方法,有点类似其他语言中 class 的成员函数。
注意:方法的接收器必须为命名类型,不能为基础类型,也不可以为接口,如果需要使用基础类型作为接收器,需要使用 type
重新定义一种类型,如:
type testint int // 使用 type 定义一种 testint 类型
func (a testint) test(b int) int // test 方法为 testint 类型的方法
c := int(a) + b // 类型转换
return c
func main()
var a testint = 2
fmt.Println(a.test(3)) // 5
声明和调用
下面用一段代码来说明方法和函数在声明和调用上的区别:
type Point struct
X float64
Y float64
type Path []Point
// 函数
func Distance(p, q Point) float64
return math.Hypot(q.X-p.X, q.Y-p.Y)
// Point 类型的方法
func (p Point) Distance(q Point) float64
return math.Hypot(q.X-p.X, q.Y-p.Y)
// Path 类型的方法
func (path Path) Distance() float64
sum := 0.0
for i := range path
if i > 0
sum += path[i-1].Distance(path[i])
return sum
p := Point1, 2
q := Point4, 6
perim := Path
1, 1,
5, 1,
5, 4,
1, 1,
fmt.Println(Distance(p, q)) // "5", function call
fmt.Println(p.Distance(q)) // "5", method call Point.Distance
fmt.Println(perim.Distance()) // "12", method call Path.Distance
事实上,方法也是一种函数,只是把接收器作为第一个入参而已。
fmt.Printf("%T\\n", Point.Distance) //func(Point, Point) float64
fmt.Printf("%T\\n", p.Distance) // func(Point) float64
选择器
形如 p.Distance(q)
p.X
这种表达式叫做选择器(selector)
,选择器会根据对象 p 的类型来选择对应的方法或者结构体字段,比如说 p.Distance(q)
会调用求点距离的方法而不是求三角形周长的方法。
因此,某个结构体的方法不能和结构体的字段重名,其内部的方法也都必须有唯一的方法名,比如对于 Point 结构体,如果定义一个名为 X
的方法,则会编译错误。但是不同的类型可以有同样的方法名。
基于指针对象的方法
由于方法也是一种函数,所以我们很容易理解方法也是通过值传递的方式来拷贝参数值,因此,和函数中的处理一样,如果我们想要在方法内对一个参数进行改变,则必须使用指针对象来作为方法的接收器,如:
func (p *Point) ScaleBy(factor float64)
p.X *= factor
p.Y *= factor
r := &Point1, 2
r.ScaleBy(2)
fmt.Println(*r) // "2, 4"
// 注意,由于临时变量的内存地址无法获取,所以不能使用下面这种写法
Point1, 2.ScaleBy(2) // compile error: can't take address of Point literal
这个方法的名字是 (*Point).ScaleBy
,这是一个 (*Point)
类型的方法而不是 Point
类型的方法。在实际使用中,因为通常不会对一个结构体类型仅有只读操作,所以基本上都会用指针来作为方法的接收器。
一个很容易出错的点在于,我们需要使用一个类型的指针来作为接收器,而不能使用指针类型本身来作为接收器,举个例子:
func (p *Point) f1() // 正确
type PP *Point
func (pp PP) f2() // 编译错误
f2 这种写法会报编译错误
invalid receiver type PP (PP is a pointer type)
通过内嵌结构体扩展方法
在之前的文章中,介绍过内嵌结构体的使用,通过内嵌结构体,可以在外层结构体中直接使用内层结构体的字段,而上文我们又说了形如 p.Distance(q)
p.X
都是对象 p 的选择器,所以我们自然会想到,外层结构体也可以使用内嵌结构体的方法,类似于其他 OOP 语言中的继承思想。如下:
import "image/color"
type Point struct
X float64
Y float64
type ColoredPoint struct
Point
Color color.RGBA
red := color.RGBA255, 0, 0, 255
blue := color.RGBA0, 0, 255, 255
var p = ColoredPointPoint1, 1, red
var q = ColoredPointPoint5, 4, blue
fmt.Println(p.Distance(q.Point)) // "5"
可以看到,即使 ColoredPoint 类型没有声明 Distance 方法,也可以直接当作 Point 类型方法的接收器来使用。类比于其他的 OOP 语言,可以将 Point 看做基类,将 ColoredPoint 看做继承它的子类来理解。
但是必须强调以下两点:
- 继承只是一种理解方法,和继承思想最大的不同是,ColoredPoint 只是拥有 Point 成员并且可以使用它的字段和方法,但是 ColoredPoint 并不是 Point 类型,因此,ColoredPoint 虽然可以作为 Point 方法的接收器,但是并不能作为 Point 类型的参数使用,下列写法会报错。
p.Distance(q) // compile error: cannot use q (ColoredPoint) as Point
- 只有内嵌结构体可以实现上述用法,如果是将 Point 类型的对象作为 ColorPoint 的字段,则不成立,以下写法会报错。
type ColoredPoint struct
point Point
Color color.RGBA
fmt.Println(p.Distance(q.Point))
// 报错
// p.Distance undefined (type ColoredPoint has no field or method Distance)
// q.Point undefined (type ColoredPoint has no field or method Point, but does have point)
方法值
跟函数一样,方法也可以作为方法值使用,这种情况下调用时就不再需要指定接收器,如下:
p := Point1, 2
q := Point4, 6
distanceFromP := p.Distance // method value
fmt.Println(distanceFromP(q)) // "5"
以上是关于Golang method 方法详解的主要内容,如果未能解决你的问题,请参考以下文章