-
Notifications
You must be signed in to change notification settings - Fork 0
/
05、接口
234 lines (169 loc) · 5.26 KB
/
05、接口
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
接口
在 Go 语言中,接口就是 方法 的集合
EG1.
package main
import (
"fmt"
)
type S struct{ i int } // 声明的新的类型
func (s *S) Get() int { return s.i } // 对声明的新类型定义了新的方法
func (s *S) Push(a int) { s.i = a }
type I interface { // 声明了一个接口
Get() int // 接口里有 不含有 接受者 的方法名和类型
Push(int)
}
func main() {
var in I // 创建了一个 接口 的对象
in = &S{6} // 为该接口传入了一个类型的值
// 注意,这里使用了等号,暗示该 struct 类型和该 interface 类型是等价的
fmt.Printf("%v\n", in.Get()) // 使用该接口可识别的方法
in.Push(1)
fmt.Printf("%v\n", in.Get())
}
到底什么是接口,我个人认为
1、从接口自身来看,接口是 类型 的标准
但凡由任何一个类型实现了某个接口所有的方法
那么这个类型就可被这个类型识别
2、从类型的角度来看,接口是多种类型的整合
有了接口,在 使用 接口涵盖的方法的时候
不需要考虑方法实际的接收者是谁
3、总的来看
接口的一端是数据的操作(也就是不含接收者信息的方法)
接口的另一端是数据(也就是某种类型)
EG2.【拓展 EG1】
文件 firstInter.go
/*测试 接口 的意义*/
package firstInter
import (
"fmt"
"strconv"
)
// 具有 Get 和 Push 方法的接口
type SI interface {
Get() string
Push(string)
String() string
}
// 定义了一个 S1 类型,为结构体,包含一个整型变量
type S1 struct {
I int
}
// 定义了 S1 类型的 Get 方法,获取了它所含有的整型数据
func (s *S1) Get() string {
return strconv.Itoa(s.I)
}
// 定义了 S1 类型的 Push 方法,可以修改它所含有的整型数据
func (s *S1) Push(a string) {
num, err := strconv.Atoi(a)
if err == nil {
s.I = num
}
}
// 定义了 S1 类型的 String 方法,返回了应该返回的字符串
func (s *S1) String() string {
return strconv.Itoa(s.I)
}
// 定义了一个 S2 类型,为结构体,包含一个字符串变量
type S2 struct {
S string
}
// 定义了 S2 类型的 Get 方法,获取了它所含有的字符串数据
func (s *S2) Get() string {
return s.S
}
// 定义了 S1 类型的 Push 方法,可以修改它所含有的字符串数据
func (s *S2) Push(a string) {
s.S = a
}
// 定义了 S1 类型的 String 方法,返回了应该返回的字符串
func (s *S2) String() string {
return s.S
}
// 创建一个函数,运行接口 SI 所有的方法
func Run(tI SI) {
fmt.Printf("%v\n", tI.Get()) // 注意, tI 不再关心方法的接收者到底是谁
tI.Push("3") // 这种理念将降低代码的组织难度
fmt.Printf("%v\n", tI.Get())
}
测试文件 firstInter_test.go
// firstInter 单元测试文件
package firstInter
var s1 *S1 = &S1{1}
var s2 *S2 = &S2{"???"}
var l []SI = []SI{s1, s2} // 这里并没有出现编译错误,说明 *S1 和 *S2 与 SI 类型是等价的
// 又由于 interface 也是一种 type,可以直接建立一个这个列表
func Example() {
for _, item := range l {
Run(item)
}
//Output:
//1
//3
//???
//3
}
一个接口可以包含另一个接口的所有方法,那个被包含的接口就被成为 插入接口
EG3.
type A interface{ // 接口 A
Amethod() // 接口 A 的签名方法 Amethod
}
type B interface{ // 接口 B
Bmethod() // 接口 B 特有的方法 Bmethod
A // 插入 接口 B 的 接口 A,接口 A 称为 接口 B 的插入接口
// 此时接口 B 直接具有 接口 A 签名的方法
}
由于存在多种类型的方法都满足同一个 interface
所以我们需要有一种方法来判断一个 interface 中的变量真实的类型
有两种主要的方法作判断
<1> type switch
switch <接口实例>.(type){ // switch 和 .(type) 是关键字
case <类型1>:
[要执行的动作1]
case <类型2>:
[要执行的动作2]
default:
[都不匹配时要执行的动作]
}
EG4.【接EG2】
func Example_2() {
for _, item := range l {
s := ""
switch item.(type) { // 类型判断
case *S1: // 注意,*S1 和 S1 是不同的类型,不要写错
s = "*S1"
case *S2:
s = "*S2"
}
fmt.Printf("Type: %s\n", s)
}
//Output:
//Type: *S1
//Type: *S2
}
<2> type assertion
<类型实例名>, ok := <接口实例>.(<预判类型>)
若确实为该类型,则 <类型实例名> 取到非 nil 的值,ok 为 true
若并非为该类型,则 <类型实例名> 为 nil, ok 为 false
EG4.【接EG3】
func Example_3() {
for _, item := range l {
if to, ok := item.(*S1); ok {
fmt.Printf("%v\n", to.Get())
} else if to, ok := item.(*S2); ok {
fmt.Printf("%v\n", to.Get())
}
}
//Output:
// 3
// 3
}
由于 空接口 interface{} 不包含任何的方法,所以 Go 语言中任何一个类型都实现了 interface{} 这个接口
我们可以使用 interface{} 这个接口作为通用类型
EG5.
// 一个可以接受任何类型的超级切片
var uniSlice []interface{} = []interface{}{1,"aaaa",'c',[]int{1,2,3}}
注意,
<1> 不可将接口作为方法的接收者
<1> 不可以使用接口指针,防止循环指针
特殊接口的命名惯例
对于只有一个方法的接口,接口名一般为 方法名+er,比如 Reader Writer Formatter