接口
多态是指代码可以根据类型的具体实现采取不同的行为
实现
接口是用来定义行为的类型。所定义的行为不由接口直接实现,而是通过自定义类型的方法来实现。
接口内部布局:
方法集
方法集定义了接口的接收规则,是有接收者的函数。方法集定义了一组关联到给定类型(struct)的实例类型所对应的方法。(类似与java中new出来的对象)
// I 定义接口,仅仅是方法的集合
type I interface {
Get() int
Put(v int)
}
// S 定义了一个类型
type S struct{
i int
}
// Get 定义类型所拥有的方法
func (p *S) Get() int{
return p.i
}
// Put
func (p *S) Put(v int){
p.i = v
}
// 定义通用的调用方法
func f(p I){
fmt.Println(p.Get())
p.Put(1)
}
func main(){
s := S{
i: 1
}
f(s) // 无法编译通过
/**
/listing36.go:32: 不能将 u(类型是 user)作为 sendNotification 的参数类型 notifier:
user 类型并没有实现 notifier(notify 方法使用指针接收者声明)
*/
f(&s) //可以编译通过
对于错误:notify method has pointer receiver
由于接口方法集定义了一组关联到给定类型的值或者指针
的函数接收者
(方法),定义方法时使用的接收者的类型(值或者指针)决定了这个方法是关联到值,还是关联到方法,还是两个都关联。
Go语言定义方法集规则
从实例类型值所绑定的方法集来看,有没有实现接口的方法。
Values | Methods Receivers |
---|---|
T | (t T) |
*T | (t T) & (t *T) |
- T类型值的方法集只包含值接收者声明的方法
- T类型指针的方法集既包括值接收者和指针接收者所声明的方法
从接收者类型角度来看某个实例类型值(值或者指针)所对应的方法集(看有没有方法实现了接口):
Methods Receivers | Values |
---|---|
(t T) | T & *T |
(t *T) | *T |
- 如果使用指针接收者来实现一个接口,那么只有指向那个类型的指针才能被interface接收
- 如果使用值接收者来实现一个接口,那么那个类型的值和指针都能被interface接收
多态
// 这个示例程序使用接口展示多态行为
02 package main
03
04 import (
05 "fmt"
06 )
0708 // notifier 是一个定义了
09 // 通知类行为的接口
10 type notifier interface {
11 notify()
12 }
13
14 // user 在程序里定义一个用户类型
15 type user struct {
16 name string
17 email string
18 }
19
20 // notify 使用指针接收者实现了 notifier 接口
21 func (u *user) notify() {
22 fmt.Printf("Sending user email to %s<%s>\n",
23 u.name,
24 u.email)
25 }
26
27 // admin 定义了程序里的管理员
28 type admin struct {
29 name string
30 email string
31 }
32
33 // notify 使用指针接收者实现了 notifier 接口
34 func (a *admin) notify() {
35 fmt.Printf("Sending admin email to %s<%s>\n",
36 a.name,
37 a.email)
38 }
39
40 // main 是应用程序的入口
41 func main() {
42 // 创建一个 user 值并传给 sendNotification
43 bill := user{"Bill", "bill@email.com"}
44 sendNotification(&bill)
45
46 // 创建一个 admin 值并传给 sendNotification
47 lisa := admin{"Lisa", "lisa@email.com"}
48 sendNotification(&lisa)
49 }
50
51 // sendNotification 接受一个实现了 notifier 接口的值
52 // 并发送通知
53 func sendNotification(n notifier) {
54 n.notify()
55 }
最后,可以在代码清单 5-49 中看到这种多态的行为。 main 函数的第 43 行创建了一个 user类型的值,并在第 44 行将该值的地址传给了 sendNotification 函数。这最终会导致执行 user类型声明的 notify 方法。之后,在第 47 行和第 48 行,我们对 admin 类型的值做了同样的事情。最终,因为 sendNotification 接受 notifier 类型的接口值,所以这个函数可以同时执行 user 和 admin 实现的行为。