go语言基础方法和接收器

 阅读大约需要2分钟

go语言基础方法和接收器

在Go语言中有一个概念和函数极其相似叫做方法,Go语言的方法其实是作用在接收者(receiver)上的一个函数,接收者是某种非内置类型的变量,因此方法是一种特殊类型的函数。

方法和函数的不同点:
  • 函数和方法声明的方式不同

  • 函数可以被当作参数传递,方法则不行

  • 函数可以匿名,方法则不行

  • 函数参数为值类型时,不能将指针类型的数据直接传递,反之亦然。

  • 方法接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。

方法的声明和普通函数的声明类似,只是在函数名称前面多了一个参数,这个参数把这个方法绑定到这个参数对应的类型上。

func (t Type) methodName(parameter list) {

}

上面的代码片段创建了一个接收器类型为Type的方法methodName。

  • 接收器变量t:接收器中的参数变量名在命名时,官方建议使用接收器类型名的第一个小写字母,而不是 self、this 之类的命名。 例如,Socket类型的接收器变量应该命名为s,Connector类型的接收器变量应该命名为c等。

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

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

方法不仅仅可以隶属于结构体类型,还可以隶属于非接口、非指针类型的其它任何自定义类型。

我们可以用Go语言的type,来定义一个和int具有同样功能的类型。这个类型不能看成是int类型的别名,它们属于不同的类型,不能直接相互赋值。

package main

import "fmt"

// 将newInt定义为int类型
type newInt int

func (n newInt) Add(b newInt) newInt {
	return n + b
}

func main() {
	var a newInt
	a = 100
	fmt.Println(a)        // 100
	fmt.Printf("%T\n", a) // main.newInt
	fmt.Println(a.Add(10))
}

方法是特殊的函数,定义在某一特定的类型上,通过类型的实例来进行调用,这个实例叫接收者。

接收者必须有一个显式的名字,这个名字必须在方法中被使用

接收者类型必须在和方法同样的包中被声明

同一个类型的方法名是不允许重复的

Go语言不允许为简单的内置类型添加方法,下面定义的方法是非法的。

func (a int) Add(b int) {
	fmt.Println(a+b)
}

既然已经有函数,为什么还要有方法呢?

  • Go不是纯粹的面向对象编程语言,而且Go不支持类,因此基于类型的方法是一种实现和类相似行为的途径。

  • 相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。

假设我们有一个Square和Circle结构体,可以在Square和Circle上分别定义一个Area方法,详见下面的程序。

package main

import (
	"fmt"
	"math"
)

type Rectangle struct {
	length int
	width  int
}

type Circle struct {
	radius float64
}

func (r Rectangle) Area() int {
	return r.length * r.width
}

func (c Circle) Area() float64 {
	return math.Pi * c.radius * c.radius
}

func main() {
	r := Rectangle{
		length: 10,
		width:  10,
	}
	fmt.Printf("Area of rectangle %d\n", r.Area())
	c := Circle{
		radius: 10,
	}
	fmt.Printf("Area of circle %f", c.Area())
}
指针接收器与值接收器

值接收器和指针接收器之间的区别在于指针接收器的方法内部的改变对于调用者是可见的,而值接收器是不可见的。

package main

import "fmt"

type Person2 struct {
	Name string
	Age  int
}

func (p Person2) SetName(name string)  {
	p.Name=name
}

func (p *Person2) SetAge(age int)  {
	p.Age=age
}

func (p *Person2) getName() string {
	return p.Name
}

func (p *Person2) getAge() int {
	return p.Age
}


func main() {
	p1:=new(Person2)
	p1.SetName("John")
	p1.SetAge(18)
	fmt.Println(p1.getName())
	fmt.Println(p1.getAge())

	p2:=Person2{}
	p2.SetName("bruce")
	p2.SetAge(19)  //使用值类型来调用指针接收器
	fmt.Println(p2.getName())
	fmt.Println(p2.getAge())
}

运行结果如下:


18

19

上面分别创建了指针类型的实例p1和值类型的实例p2,但无论是p1还是p2,它们调用SetName()方法设置的name值都没有影响原始实例中的name值,所以getName()都输出空字符串, 而它们调用setAge()方法设置的age值都影响了原始实例中的age值。

我们通过p2来调用指针接收器的方法SetAge这是被允许的,为了方便Go语言把代码p2.SetAge(19)解释为(&p2).SetAge(19)

如何选择接收器类型

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

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

有修改成员变量的需求,用指针类型的接收器。