Go语言基础、实战 -- 9. 方法

1. 简介

    在面向对象编程中,一个对象其实就是一个简单的值或者一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,称为方法。本质上,方法是和一种类型关联的函数。(类似于C++的成员函数,只有这种类型的对象才可以调用)

 

2. 定义方式

    在Go语言中,方法总是绑定对象实例,并隐式地将实例作为第一实参,方法的定义语法如下:

func (v Type) MethodName() {

}

3. 实际例子

    结构体作为接收者

    首先,声明一个新的类型:

type Band struct {
	member	int
	name	string
	vocal	string
	guitar	string
	bass	string
	drummer	string
}

    然后,定义方法:

func (b Band) Name() string {
	return b.name
}

    最后,调用方法:只需要声明一个Band类型的对象,就可以直接调用这个方法:

band := &Band{
    member:  5,
    name:    "SoBand",
    vocal:   "AShin",
    guitar:  "Monster;Stone",
    bass:    "Matthew",
    drummer: "Ming",
}

fmt.Println(band.Name())    // 输出结果为:SoBand

    

    基础类型作为接收者

type LiarInt int

func (i LiarInt) Get() int {
    res := int(i) + 5
    return res
}

func main() {
    var a LiarInt = 5
    fmt.Printf("[a = %d]\n", a.Get())    // 输出结果为:10
}

    这里为什么要新声明一种类型,而不直接用int呢?

    因为Go语言中,定义某种类型的方法,接收者类型的定义与其方法的定义必须在同一个包中,否则会报错。

 

4. 指针接收者&值接收者

    在上面的例子中,都是用的值作为接收者,还可以用指针作为接收者。其区别与函数传参时使用值传递或指针传递一样。

    值接收者,在方法内部对其进行修改,方法调用完成后,不会对调用者产生影响。

    指针接收者,在方法内部对其进行修改,方法调用完成后,调用者也会随之改变。

    请看下面的例子:

// 值接收者,这里是值传递,实际上并没有对它进行修改
func (b Band) ChangeName_Err(name string) {
    b.name = name
}

// 指针接收者
func (b *Band) ChangeName(name string) {
    b.name = name
}

func main() {
    band := &Band{
        member:  5,
        name:    "SoBand",
        vocal:   "AShin",
        guitar:  "Monster;Stone",
        bass:    "Matthew",
        drummer: "Ming",
    }
    fmt.Println(band.Name())        // 输出结果为:SoBand

    // 调用值传递的方法,调用完成后,band对象实际上并没有改变
    band.ChangeName_Err("MAYDAY")
    fmt.Println(band.Name())        // 输出结果为:SoBand

    // 调用指针传递的方法,调用完成后,band对象发生了改变
    band.ChangeName("MAYDAY")
    fmt.Println(band.Name())        // 输出结果为:MAYDAY
}

    但它与函数传参不同的是,在函数传参时,它的类型必须一致,比如函数的参数定义的是指针,那么在传参就必须是指针。

    而在方法的调用中,则没有这种限制。不管我们调用者是值类型还是指针类型的对象,它都可以随意去调用接收者为值类型或指针类型的方法。

    如上的例子中,ChangeName_Err 方法的接收者为值类型,但我们的调用者实际上是指针类型的对象,依然可以调用。同理,如果我们创建一个band对象是值类型的,去调用接收者为指针类型的 ChangeName 方法,也能成功,且效果一样。

    对上面的例子稍作更改:

func main() {
    p_band := &Band{
        member:  5,
        name:    "SoBand",
        vocal:   "AShin",
        guitar:  "Monster;Stone",
        bass:    "Matthew",
        drummer: "Ming",
    }

    var band Band = *p_band
    fmt.Println(band.Name())        // 输出结果为:SoBand

    // 调用值传递的方法,调用完成后,band对象实际上并没有改变
    band.ChangeName_Err("MAYDAY")
    fmt.Println(band.Name())        // 输出结果为:SoBand

    // 调用指针传递的方法,调用完成后,band对象发生了改变
    band.ChangeName("MAYDAY")
    fmt.Println(band.Name())        // 输出结果为:MAYDAY
}

    总结:接收者为值类型或者指针类型,不会影响我们的调用方式,不管我们调用者的对象是值类型,还是指针类型,都可以随意的调用它们。

    所以,当我们希望方法内的修改对调用者同样产生影响,或者避免值拷贝的开销时,应当考虑使用指针类型的接收者。

 

5. 匿名字段

    当一个结构体作为匿名字段被嵌套到另外一个结构体中,跟字阶提升一样,我们同样可以直接调用被嵌套的结构体中的方法。

// Band作为匿名字段被嵌套在Show中
type Show struct {
    Time    string
    Arena   string
    Song    string
    Band
}

func main() {
    show := &Show{"18:00", "Bird's Nest", "Embrace", band}
    bandName := show.Name()                 // Show的对象直接调用其匿名字段Band的方法
    fmt.Printf("[Shower: %s]\n", bandName)	// 输出结果为: MAYDAY
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值