Go 语言不是面向对象的语言,它里面不存在类的概念,结构体正是类的替代品。类可以附加很多成员方法,结构体也可以。

  1. package main
  2. import "fmt"
  3. import "math"
  4. type Circle struct {
  5. x int
  6. y int
  7. Radius int
  8. }
  9. // 面积
  10. func (c Circle) Area() float64 {
  11. return math.Pi * float64(c.Radius) * float64(c.Radius)
  12. }
  13. // 周长
  14. func (c Circle) Circumference() float64 {
  15. return 2 * math.Pi * float64(c.Radius)
  16. }
  17. func main() {
  18. var c = Circle {Radius: 50}
  19. fmt.Println(c.Area(), c.Circumference())
  20. // 指针变量调用方法形式上是一样的
  21. var pc = &c
  22. fmt.Println(pc.Area(), pc.Circumference())
  23. }

Go 语言不喜欢类型的隐式转换,所以需要将整形显示转换成浮点型,不是很好看,不过这就是 Go 语言的基本规则,显式的代码可能不够简洁,但是易于理解。
Go 语言的结构体方法里面没有 self 和 this 这样的关键字来指代当前的对象,它是用户自己定义的变量名称,通常我们都使用单个字母来表示。
Go 语言的方法名称也分首字母大小写,它的权限规则和字段一样,首字母大写就是公开方法,首字母小写就是内部方法,只能归属于同一个包的代码才可以访问内部方法。
结构体的值类型和指针类型访问内部字段和方法在形式上是一样的。这点不同于 C++ 语言,在 C++ 语言里,值访问使用句点 . 操作符,而指针访问需要使用箭头 -> 操作符。

结构体指针方法

如果使用上面的方法形式给 Circle 增加一个扩大半径的方法,你会发现半径扩大不了。

  1. func (c Circle) expand() {
  2. c.Radius *= 2
  3. }

这是因为上面的方法和前面的 expandByValue 函数是等价的,只不过是把函数的第一个参数挪了位置而已,参数传递时会复制了一份结构体内容,起不到扩大半径的效果。这时候就必须要使用结构体的指针方法

  1. func (c *Circle) expand() {
  2. c.Radius *= 2
  3. }

结构体指针方法和值方法在调用时形式上是没有区别的,只不过一个可以改变结构体内部状态,而另一个不会。指针方法使用结构体值变量可以调用,值方法使用结构体指针变量也可以调用。
通过指针访问内部的字段需要 2 次内存读取操作,第一步是取得指针地址,第二部是读取地址的内容,它比值访问要慢。但是在方法调用时,指针传递可以避免结构体的拷贝操作,结构体比较大时,这种性能的差距就会比较明显。
还有一些特殊的结构体它不允许被复制,比如结构体内部包含有锁时,这时就必须使用它的指针形式来定义方法,否则会发生一些莫名其妙的问题。