Go-学会方法的基本使用

本节重点:

  • 学会方法的基本使用

方法主要源于 OOP 语言,在传统面向对象语言中 (例如 C++), 我们会用一个“类”来封装属于自己的数据和函数,这些类的函数就叫做方法。

虽然 Go 不是经典意义上的面向对象语言,但是我们可以在一些接收者(自定义类型,结构体)上定义函数,同理这些接收者的函数在 Go 里面也叫做方法。

方法的声明

方法(method)的声明和函数很相似, 只不过它必须指定接收者:

func (t T) F() {}

注意:

  • 接收者的类型只能为用关键字 type 定义的类型,例如自定义类型,结构体。
  • 同一个接收者的方法名不能重复 (没有重载),如果是结构体,方法名还不能和字段名重复。
  • 值作为接收者无法修改其值,如果有更改需求,需要使用指针类型。

简单例子

现在我们写一个简单的程序,在结构类型上创建一个方法并调用它。

package main
 
import (  
    "fmt"
)
 
type Employee struct {  
    name     string
    salary   int
    currency string
}
 
func (e Employee) displaySalary() {  
    fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
 
func main() {  
    emp1 := Employee {
        name:     "Sam Adolf",
        salary:   5000,
        currency: "$",
    }
    emp1.displaySalary()
}

在上面程序的第 13 行中,我们在 Employee 结构体类型上创建了一个方法 displaySalarydisplaySalary() 方法可以访问其中的接收者 e。我们使用收款人 e,打印员工的 namecurrency 和 salary

我们在第23行使用该方法调用了 emp1.displaySalary()

上面程序的输出是 :Salary of Sam Adolf is $5000

指针接收器与值接收器型

前面都是带有值接收器的方法。可以使用指针接收器创建方法。值和指针接收器之间的区别在于,在具有指针接收器的方法内部所做的更改对调用者是可见的,而在值接收器中则不然。让我们在程序的帮助下理解这一点。

package main
 
import (  
    "fmt"
)
 
type Employee struct {  
    name string
    age  int
}
 
/*
Method with value receiver  
*/
func (e Employee) changeName(newName string) {  
    e.name = newName
}
 
/*
Method with pointer receiver  
*/
func (e *Employee) changeAge(newAge int) {  
    e.age = newAge
}
 
func main() {  
    e := Employee{
        name: "Mark Andrew",
        age:  50,
    }
    fmt.Printf("Employee name before change: %s", e.name)
    e.changeName("Michael Andrew")
    fmt.Printf("\nEmployee name after change: %s", e.name)
 
    fmt.Printf("\n\nEmployee age before change: %d", e.age)
    (&e).changeAge(51)
    fmt.Printf("\nEmployee age after change: %d", e.age)
}

在上面的程序中,changeName 方法有一个值接收器 (e Employee),而 changeAge 方法有一个指针接收器 (e *Employee)。在 changeName 中对 Employee 结构体的 name 字段所做的更改对调用者是不可见的,因此程序会在方法 e 之前和之后打印相同的 name。因为 changeAge 方法有一个指针接收器 (e *Employee),所以在方法调用 (&e) 之后对 age 字段所做的更改 (51) 将对调用者可见。这个程序打印:

Employee name before change: Mark Andrew  
Employee name after change: Mark Andrew
 
Employee age before change: 50  
Employee age after change: 51 

何时使用指针接收与值接收

通常,当调用者应该可以看到方法内对接收器所做的更改时,可以使用指针接收器。

指针接收器也可用于复制数据结构成本高昂的地方。考虑一个具有许多字段的结构。将此结构用作方法中的值接收器将需要复制整个结构,这将是昂贵的。在这种情况下,如果使用指针接收器,则不会复制结构,而在方法中只会使用指向它的指针。

在所有其他情况下,可以使用值接收器。

命名冲突

a. 接收者定义的方法名不能重复, 例如:

package main

type T struct{}

func (T) F()         {}
func (T) F(a string) {}

func main() {
    t := T{}
    t.F()
}

运行代码我们会得到 method redeclared: T.F 类似错误。

b. 结构体方法名不能和字段重复,例如:

package main

type T struct{
  F string
}

func (T) F(){}

func main() {
    t := T{}
    t.F()
}

运行代码我们会得到 : type T has both field and method named F 类似错误。

同一个接收者的方法名不能重复 (没有重载);如果是结构体,方法名不能和字段重复。

接收者可以同时为值和指针

在 Go 语言中,方法的接收者可以同时为值或者指针,例如:

package main

type T struct{}

func (T) F()  {}
func (*T) N() {}

func main() {
    t := T{}
    t.F()
    t.N()

    t1 := &T{} // 指针类型
    t1.F()
    t1.N()
}

可以看到无论值类型 T 还是指针类型 &T 都可以同时访问 F 和 N 方法。

值和指针作为接收者的区别

同样我们先看一段代码:

package main

import "fmt"

type T struct {
    value int
}

func (m T) StayTheSame() {
    m.value = 3
}

func (m *T) Update() {
    m.value = 3
}

func main() {
    m := T{0}
    fmt.Println(m) // {0}

    m.StayTheSame()
    fmt.Println(m) // {0}

    m.Update()
    fmt.Println(m) // {3}
}

运行代码输出结果为:

{0}
{0}
{3}

值作为接收者(T) 不会修改结构体值,而指针 *T 可以修改。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极简网络科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值