概述
-
结构体嵌套结构体
Go 中在 结构体 A 嵌套另外一个 结构体 B 见的很多,通过嵌套,可以扩展A的能力。
A不仅拥有了B的属性,还拥有了B的方法,这里面有一个字段提升的概念。
-
结构体嵌套接口
结构体里嵌套接口的目的:
当前结构体实例可以用所有实现了该接口的其他结构体来初始化(即使他们的属性不完全一致)
接口嵌套实例
在结构体(struct)中内嵌接口(interface),定义如下接口
1
2
3
4
5
6
|
// 接口:一组方法的集合
// OpenCloser 接口定义两个方法 返回 error
type OpenCloser interface {
Open() error
Close() error
}
|
定义如下结构体,内嵌了接口 OpenCloser
1
2
3
4
5
|
type AutoDoor struct {
OpenCloser // 匿名接口
delay int // 延迟多长时间开启
msg string // 自动开启时的警报
}
|
当初始化结构体AutoDoor时,需要传入一个实现此接口OpenCloser的结构体赋值,所以定义如下结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
type Door struct {
open bool // 门的状态是否开启
lock bool // 门的状态是否上锁
}
func (d *Door) Open() error {
fmt.Println("door open...")
d.open = true
return nil
}
func (d *Door) Close() error {
fmt.Println("door close...")
d.open = false
return nil
}
|
当初始化 AutoDoor 结构体时,
|
door := &AutoDoor{&Door{false, false}, 3, "warning"}
|
传入的是一个 Door 结构体的指针类型。
完整的程序如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
package main
import (
"fmt"
"strconv"
"time"
)
// 接口:一组方法的集合
// OpenCloser 接口定义两个方法 返回 error
type OpenCloser interface {
Open() error
Close() error
}
type Door struct {
open bool // 门的状态是否开启
lock bool // 门的状态是否上锁
}
func (d *Door) Open() error {
fmt.Println("door open ...")
d.open = true
return nil
}
func (d *Door) Close() error {
fmt.Println("door close ...")
d.open = false
return nil
}
type AutoDoor struct {
OpenCloser // 匿名接口
delay int // 延迟多长时间开启
msg string // 自动开启时的警报
}
func (a *AutoDoor) Open() error {
fmt.Println("Open after " + strconv.Itoa(a.delay) + " seconds")
time.Sleep(time.Duration(a.delay) * time.Second)
fmt.Println("Door is opening:" + a.msg)
return nil
}
func main() {
door := &AutoDoor{
OpenCloser: &Door{
open: false,
lock: false,
},
delay: 3,
msg: "warning",
}
door.Open()
if v, ok := door.OpenCloser.(*Door); ok { // 类型断言
fmt.Println("door.Open()=",v)
}
door.OpenCloser.Open()
if v, ok := door.OpenCloser.(*Door); ok { //类型断言
fmt.Println("door.OpenCloser.Open()=",v)
}
//door.OpenCloser.Close()
door.Close()
if v, ok := door.OpenCloser.(*Door); ok { //类型断言
fmt.Println("door.Close()=",v)
}
}
|
运行结果如下:
➜ struct_test_demo go run struct_in_interface2.go
Open after 3 seconds
Door is opening:warning
door.Open()= &{false false}
door open ...
door.OpenCloser.Open()= &{true false}
door close ...
door.Close()= &{false false}
注意的是,AutoDoor 并未定义Close 方法,而是通过内嵌接口的方式获得了该方法。
接口嵌套应用
golang的context标准库就是这样实现的context之间的嵌套。
字段提升
下面是 Go 语言中内部类型方法集提升的规则:
http://golang.org/ref/spec#Method_sets
https://www.goinggo.net/2014/05/methods-interfaces-and-embedded-types.html
给定一个结构体类型 S 和一个命名为 T 的类型,方法提升像下面规定的这样被包含在结构体方法集中:
如果 S 包含一个匿名字段 T,S 和 *S 的方法集都包含接受者为 T 的方法提升。
这条规则说的是当我们嵌入一个类型,嵌入类型的接受者为值类型的方法将被提升,可以被外部类型的值和指针调用。
对于 *S 类型的方法集包含接受者为 *T 的方法提升
这条规则说的是当我们嵌入一个类型,可以被外部类型的指针调用的方法集只有嵌入类型的接受者为指针类型的方法集,也就是说,当外部类型使用指针调用内部类型的方法时,只有接受者为指针类型的内部类型方法集将被提升。
如果 S 包含一个匿名字段 *T,S 和 *S 的方法集都包含接受者为 T 或者 *T 的方法提升
这条规则说的是当我们嵌入一个类型的指针,嵌入类型的接受者为值类型或指针类型的方法将被提升,可以被外部类型的值或者指针调用。
这就是语言规范里方法提升中仅有的三条规则,根据这个推导出一条规则:
如果 S 包含一个匿名字段 T,S 的方法集不包含接受者为 *T 的方法提升。
这条规则说的是当我们嵌入一个类型,嵌入类型的接受者为指针的方法将不能被外部类型的值访问。
参考资料:
Go结构体里嵌套接口
Go结构体嵌入接口类型
golang中结构体嵌套接口实现送耦合
源文地址:http://www.manoner.com/post/GoLand/Go%E7%BB%93%E6%9E%84%E4%BD%93%E4%B8%AD%E5%B5%8C%E5%A5%97%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E6%9D%BE%E8%80%A6%E5%90%88/
|