抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

方法集

Golang中每一个类型所对应的方法,我们把它称为方法集,按方法接收者来划分可以分为:实体类型和指针类型。如下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
type Student struct {
Age int
Name string
}
// 实体类型方法接收者
func (s Student) showName() {
fmt.Println("name="+s.Name)
}

// 指针类型方法接收者
func (s *Student) setName(name string) {
s.Name = name
}
  • 实体类型Student接收者方法集包括:showName()
  • 指针类型Student接收者方法集包括:showName(),setName()

Go中规定了如下规则

  • 类型T的方法集包含receiver T的方法
  • 类型*T的方法集包含receiver T和receiver *T的方法

但需要注意的是实体类型也可以调用指针类型接收者的方法的,如下例子:

1
2
s := Student{}
s.setName("Tom") // (&s).setName("Tom")

这是因为编译器会进行自动类型转换,所以无论是实体类型还是指针类型都可以随意调用自身的任何方法。因此我们类型调用自身方法时无需在意方法集的概念,方法集需要和接口一起来使用。

方法集和接口

接口代表一个或者多个方法签名的集合,因此只要任何类型具有该接口所有方法签名的话,就可以说它实现了该接口,无需类似Java/C++使用显示implement等关键字去声明。

一个非空方法接口(iface)是由接口表和数据指针所构成的。那么使用实体类型或者指针类型去实现接口有什么区别吗?

  • 如果是实体类型去实现接口所定义的方法集,那么T和*T都可以说实现了该接口
  • 如果是指针类型去实现接口所定义的方法集,那么只有*T才实现了该接口

如下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type IPeople interface {
setName(name string)
}

type People struct {
Age int
Name string
}

func (p People) showName() {
fmt.Println("name=" + p.Name)
}

func (p *People) setName(name string) {
p.Name = name
}

func main() {
people := People{}
var i IPeople = people // 编译失败:People does not implement IPeople (setName method has pointer receiver)

}

可以看到编译时报People实体类型没有实现IPeople接口,可以修改为var i IPeople = &people或者将setName方法接收者修改为实体类型。

方法集和组合

总所周知,Go中并没有类似Java/C++等传统OOP的extends关键字,这是因为Go提倡使用组合来实现继承的行为,如下面的例子。

1
2
3
4
5
6
7
8
type People struct {
Age int
Name string
}

type Student struct {
People
}

那么实体类型组合和指针类型组合又有什么区别呢?

1
2
3
4
5
6
7
type Student1 struct {
People
}

type Student2 struct {
*People
}

首先是默认值不同

  • struct是复合类型,因此默认值是所包含类型的零值
  • 指针是引用类型,零值为nil

再其次就是方法集的不同

  • 类型S包含匿名T类型,则S类型和*S类型都包含T的方法集
  • 类型S包含匿名* T类型, 则S类型和*S类型都包含T和 *T的方法集
  • 无论类型S包含匿名*T类型还是T类型, *S都包含T和 *T的方法集