5.4. 接收器-方法作用的目标

接收器的格式如下:

func (接收器变量 接收器类型) 方法名(参数列表)(返回参数){
函数体
}

· 接收器变量:官方建议使用接收器类型名第一个小写字母。

· 接收器类型:接收器类型和参数类似,可以是指针类型和非指针类型

· 方法名、参数列表、返回参数:格式与函数定义一致。

接收器根据接收器的类型可以分为指针接收器、非指针接收器,两种接收器在使用时会产生不同的效果。

5.4.1. 1.指针类型的接收器

package main

import "fmt"

// 定义属性结构
type Property struct {
    value int
}

// 设置属性值
func (p *Property) SetValue(v int) {
    // 修改p的成员变量
    p.value = v
}

// 获取属性值
func (p *Property) Value() int {
    return p.value
}

func main() {
    // 实例化属性
    p := new(Property)
    // 设置值
    p.SetValue(100)
    // 打印值
    fmt.Println(p.Value())  //100
}

5.4.2. 2.理解非指针类型的接收器

当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份,在非指针接收器的方法中可以获取接收器的成员值,但修改后无效。

package main

import "fmt"

// 定义点结构
type Point struct {
    X int
    Y int
}

// 非指针接收器的加方法
func (p Point) Add(other Point) Point {
    // 成员值与参数相加后返回新的结构
    return Point{p.X + other.X, p.Y + other.Y}
}
func main() {
    // 初始化点
    p := Point{1, 1}
    p2 := Point{2, 2}
    // 与另外一个点相加
    result := p.Add(p2)
    // 输出结果
    fmt.Println(result) //{3 3}
}

5.4.3. 3.指针和非指针接收器的使用

  • 小对象由于值复制时的速度较快,所以适合使用非指针接收器。

  • 大对象因为复制性能较低,适合使用指针接收器,在接收器和参数间传递时不进行复制,只是传递指针。

5.4.4. 4.为类型添加方法

4.1 为基本类型添加方法

判断一个值是否为0

func main() {
    var v int = 0
    if v == 0 {
        fmt.Println("v = 0")
    }
}

如果v当做整型对象,那么就可以增加一个IsZero()方法。

package main

import "fmt"

// 将int定义为MyInt,将int定义为自定义的MyInt类型
type MyInt int

// 为MyInt添加IsZero()方法,该方法使用了非指针接收器,数值类型没有必要使用指针接收器
func (m MyInt) IsZero() bool {
    return m == 0
}

//为MyInt添加Add()方法,将m从MyInt类型转换为int类型后再计算
func (m MyInt) Add(other int) int {
    return other + int(m)
}
func main() {
    var b MyInt
    fmt.Println(b.IsZero())         //true
    b = 1
    fmt.Println(b.Add(2))       //3

}

4.2 http包中的类型方法

Go语言提供的http包里也大量使用了类型方法

代码示例:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "strings"
)

func main() {
    client := &http.Client{}
    // 创建一个http请求
    req, err1 := http.NewRequest("POST", "http://www.163.com/", strings.NewReader("key=value"))

    //发现错误就打印并退出
    if err1 != nil {
        fmt.Println(err1)
        os.Exit(1)
        return
    }
    // 为标头添加信息
    req.Header.Add("User-Agent", "myClient")
    //开始请求
    resp, err2 := client.Do(req)
    // 处理请求的错误
    if err2 != nil {
        fmt.Println(err2)
        os.Exit(1)
        return
    }
    // 读取服务器返回的内容
    data, err3 := ioutil.ReadAll(resp.Body)
    if err3 != nil {
        fmt.Println(err2)
        os.Exit(1)
        return
    }
    fmt.Println(string(data))

    // 最后调用defer,关闭上下文
    defer req.Body.Close()
}

http.Header 就是典型的自定义类型,并拥有自己的方法。http.Header的部分定义如下:

添加一个Header非指针接收器,Add()方法需要传入2个string的值

func (h Header) Add(key, value string) {
    textproto.MIMEHeader(h).Add(key, value)
}


func (h Header) Set(key, value string) {
    textproto.MIMEHeader(h).Set(key, value)
}

func (h Header) Get(key string) string {
    return textproto.MIMEHeader(h).Get(key)

4.3 time包中的类型方法

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println(time.Second.String())
}

time.Second是一个常量,源码如下:

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)

5.4.5. 5.示例:使用事件系统实现事件的响应和处理

1.方法和函数的统一调用

package main

import "fmt"

// 声明一个结构体
type class struct {
}

// 给结构体添加Do方法,参数为整型,打印和输入参数值
func (c *class) Do(v int) {
    fmt.Println("call method do:", v)
}

// 普通函数的Do方法,参数也是整型,打印和输入参数值
func funcDo(v int) {
    fmt.Println("call function do:", v)
}
func main() {
    // 声明一个函数回调
    var delegate func(int)

    // 创建结构体的实例
    c := new(class)
    // 将回调函数设为c的Do方法
    delegate = c.Do
    // 调用
    delegate(100)       //call method do: 100

    // 将回调设为普通函数
    delegate = funcDo
    // 调用
    delegate(100)       //call function do: 100
}