Go学习笔记05-方法

什么是方法

方法其实就是一个函数,在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:

https://studygolang.com/articles/12264