什么是方法:
方法其实就是一个函数,在func这个关键字和方法名中间加入了一个特殊的接收器类型,接收器可以是结构体类型或者是非结构体类型,接收器是可以在方法的内部访问的。
下面就是创建一个方法的语法:
Go
func (t Tpye) methodName(parameter list) { }
上面的代码片段创建了一个接收器类型为Type的方法 methodName
示例:
在Employee结构体类型上创建一个displaySalary方法,displaySalart()方法在方法的内部访问了接收器e Employee
Go
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() {
employ1 := Employee{
name: "wang",
salary: 5000,
currency: "$",
}
employ1.displaySalary()
}
输出:
Go
Salary of wang is $5000
为什么已经有函数了还需要方法:
将上面的例子改写成函数:
Go
package main
import (
"fmt"
)
type Employee struct {
name string
salary int
currency string
}
func displaySalary(e Employee) {
fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
func main() {
employ1 := Employee{
name: "wang",
salary: 5000,
currency: "$",
}
displaySalary(employ1)
}
输出:
Go
Salary of wang is $5000
在上面的程序中,displaySalart 方法被转化为一个函数,Employee 结构体被当做参数传递给它,这个程序也产生完全相同的输出
为什么需要方法,有几个原因:
1、Go 不是纯粹的面向对象的编程语言,而且Go不支持类,因此,基于类型的方法是一种实现和类相似行为的途径。
2、相同的名字的方法可以定义在不同类型上,而相同名字的函数是不允许的。
指针接收器和值接收器
GO 方法里面还可以创建指针接收器的方法,值接收器和指针接收器的区别在于,在指针接收器的方法内部的改变对于调用者是可见的,然而值接收器的情况不是这样的
理解:值接收器传递的只是一个值,而指针接收器传递的是一个地址,指向该值的地址
如何选择指针接收器还是值接收器:
1、修改接收器状态:如果方法需要修改接收器的状态,Name通常应该使用指针接收器,这是因为指针接收器可以直接修改接收器指向的对象,而值接收器只是接收对象的副本,对其修改不会影响原始对象。
2、避免值拷贝:如果接收器的类型是较大的结构体或包含了大量数据的类型,而方法不需要修改接收器状态,但需要读取接收器的属性,那么应该考虑使用指针接收器,这可以避免在方法调用时发生不必要的值拷贝,提高程序的性能
3、一致性:如果在类型的方法中有一些方法需要修改接收器状态,而其他方法只是需要读取接收器的属性,那么应该保持一致性,统一使用指针接收器。
4、可空类型:如果类型的零值是合法的,并且在类型的实例在某些情况下可能为nil,那么应该使用指针接收器,这样可以更好处理nil接收器的情况,避免因为nil接收器调用方法而引发panic
示例:
Go
package main
import (
"fmt"
)
type Employee struct {
name string
age int
}
// 使用值接收器的方法
func (e Employee) changeName(newName string) {
e.name = newName
}
// 使用指针接收器的方法
func (e *Employee) changeAge(newAge int) {
e.age = newAge
}
func main() {
e := Employee{
name: "wang",
age: 20,
}
fmt.Printf("Employee name before change: %s", e.name)
e.changeName("zhang")
fmt.Printf("\nEmployee name after change: %s", e.name)
fmt.Printf("\n\nEmployee age before change: %d", e.age)
(&e).changeAge(21)
fmt.Printf("\nEmployee age after change: %d", e.age)
}
输出:
Go
Employee name before change: wang
Employee name after change: wang
Employee age before change: 20
Employee age after change: 21
在上面的程序中,changeName方法有一个值接收器(e Employee),而changeAge 方法有一个指针接收器(e *Employee). 在changeName 方法中对Employee结构体的字段name所做的改变对调用者是不可见的,因此在程序调用e.chanName("zhang")这个方法前后打印出相同的名字,由于changeAge方法值使用指针(e *Employee)接收器的,所以在调用(&e).changeAge方法对age字段做出的改变对调用者将是可见的。
在上面的程序里使用了 (&e).changeAge(21)来调用changeAge方法,由于changeAge方法有一个指针接收器,所以使用(&e)来调用这个方法,其实没有这个必要,Go语言让我们可以直接使用e.changeAge(21),会被自动解释为 (&e).changeAge(21)
在方法中使用指针接收器和在函数中使用指针参数
和值参数类似,函数使用指针参数只接受指针,而使用指针接收器的方法可以使用值接收器和指针接收器
示例:
Go
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
perimeter function output: 30
perimeter method output: 30
perimeter method output: 30
我们通过值接收器 r 来调用有指针接收器的方法 perimeter。这是被允许的,为了方便Go语言把代码 r.perimeter() 解释为 (&r).perimeter()。
在非结构体上的方法
Go
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)
}
上面的程序中,为int类型创建了一个类型别名myInt,定义了一个以myInt为接收器的方法add
输出:
Go
Sum is 15
参考: