@@ -14,6 +14,7 @@ Go语言里面设计最精妙的应该算interface,它让面向对象,内容
14
14
上面这些方法的组合称为interface(被对象Student和Employee实现)。例如Student和Employee都实现了interface:SayHi和Sing,也就是这两个对象是该interface类型。而Employee没有实现这个interface:SayHi、Sing和BorrowMoney,因为Employee没有实现BorrowMoney这个方法。
15
15
### interface类型
16
16
interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。详细的语法参考下面这个例子
17
+ ``` Go
17
18
18
19
type Human struct {
19
20
name string
@@ -82,7 +83,7 @@ interface类型定义了一组方法,如果某个对象实现了某个接口
82
83
Sing (song string )
83
84
SpendSalary (amount float32 )
84
85
}
85
-
86
+ ```
86
87
通过上面的代码我们可以知道,interface可以被任意的对象实现。我们看到上面的Men interface被Human、Student和Employee实现。同理,一个对象可以实现任意多个interface,例如上面的Student实现了Men和YoungChap两个interface。
87
88
88
89
最后,任意的类型都实现了空interface(我们这样定义:interface{}),也就是包含0个method的interface。
@@ -93,6 +94,7 @@ interface类型定义了一组方法,如果某个对象实现了某个接口
93
94
因为m能够持有这三种类型的对象,所以我们可以定义一个包含Men类型元素的slice,这个slice可以被赋予实现了Men接口的任意结构的对象,这个和我们传统意义上面的slice有所不同。
94
95
95
96
让我们来看一下下面这个例子:
97
+ ``` Go
96
98
97
99
package main
98
100
import " fmt"
@@ -169,11 +171,12 @@ interface类型定义了一组方法,如果某个对象实现了某个接口
169
171
value.SayHi ()
170
172
}
171
173
}
172
-
174
+ ```
173
175
通过上面的代码,你会发现interface就是一组抽象方法的集合,它必须由其他非interface类型实现,而不能自我实现, Go通过interface实现了duck-typing:即"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子"。
174
176
175
177
### 空interface
176
178
空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void* 类型。
179
+ ``` Go
177
180
178
181
// 定义a为空接口
179
182
var a interface {}
@@ -182,17 +185,20 @@ interface类型定义了一组方法,如果某个对象实现了某个接口
182
185
// a可以存储任意类型的数值
183
186
a = i
184
187
a = s
185
-
188
+ ```
186
189
一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!
187
190
### interface函数参数
188
191
interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数。
189
192
190
193
举个例子:fmt.Println是我们常用的一个函数,但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件,你会看到这样一个定义:
194
+ ``` Go
191
195
192
196
type Stringer interface {
193
197
String () string
194
198
}
199
+ ```
195
200
也就是说,任何实现了String方法的类型都能作为参数被fmt.Println调用,让我们来试一试
201
+ ``` Go
196
202
197
203
package main
198
204
import (
@@ -215,12 +221,14 @@ interface的变量可以持有任意实现该interface类型的对象,这给
215
221
Bob := Human{" Bob" , 39 , " 000-7777-XXX" }
216
222
fmt.Println (" This Human is : " , Bob)
217
223
}
224
+ ```
218
225
现在我们再回顾一下前面的Box示例,你会发现Color结构也定义了一个method:String。其实这也是实现了fmt.Stringer这个interface,即如果需要某个类型能被fmt包以特殊的格式输出,你就必须实现Stringer这个接口。如果没有实现这个接口,fmt将以默认的方式输出。
226
+ ``` Go
219
227
220
228
// 实现同样的功能
221
229
fmt.Println (" The biggest one is" , boxes.BiggestsColor ().String ())
222
230
fmt.Println (" The biggest one is" , boxes.BiggestsColor ())
223
-
231
+ ```
224
232
注:实现了error接口的对象(即实现了Error() string的对象),使用fmt输出时,会调用Error()方法,因此不必再定义String()方法了。
225
233
### interface变量存储的类型
226
234
@@ -233,6 +241,7 @@ interface的变量可以持有任意实现该interface类型的对象,这给
233
241
如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。
234
242
235
243
让我们通过一个例子来更加深入的理解。
244
+ ``` Go
236
245
237
246
package main
238
247
@@ -272,13 +281,14 @@ interface的变量可以持有任意实现该interface类型的对象,这给
272
281
}
273
282
}
274
283
}
275
-
284
+ ```
276
285
是不是很简单啊,同时你是否注意到了多个if里面,还记得我前面介绍流程时讲过,if里面允许初始化变量。
277
286
278
287
也许你注意到了,我们断言的类型越多,那么if else也就越多,所以才引出了下面要介绍的switch。
279
288
- switch测试
280
289
281
290
最好的讲解就是代码例子,现在让我们重写上面的这个实现
291
+ ``` Go
282
292
283
293
package main
284
294
@@ -319,21 +329,23 @@ interface的变量可以持有任意实现该interface类型的对象,这给
319
329
}
320
330
}
321
331
}
322
-
332
+ ```
323
333
这里有一点需要强调的是:`element.(type)`语法不能在switch外的任何逻辑里面使用,如果你要在switch外面判断一个类型就使用`comma-ok`。
324
334
325
335
### 嵌入interface
326
336
Go里面真正吸引人的是它内置的逻辑语法,就像我们在学习Struct时学习的匿名字段,多么的优雅啊,那么相同的逻辑引入到interface里面,那不是更加完美了。如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。
327
337
328
338
我们可以看到源码包container/heap里面有这样的一个定义
339
+ ``` Go
329
340
330
341
type Interface interface {
331
342
sort.Interface // 嵌入字段sort.Interface
332
343
Push (x interface {}) // a Push method to push elements into the heap
333
344
Pop () interface {} // a Pop elements that pops elements from the heap
334
345
}
335
-
346
+ ```
336
347
我们看到sort.Interface其实就是嵌入字段,把sort.Interface的所有method给隐式的包含进来了。也就是下面三个方法:
348
+ ``` Go
337
349
338
350
type Interface interface {
339
351
// Len is the number of elements in the collection.
@@ -344,49 +356,55 @@ Go里面真正吸引人的是它内置的逻辑语法,就像我们在学习Str
344
356
// Swap swaps the elements with indexes i and j.
345
357
Swap (i, j int )
346
358
}
347
-
359
+ ```
348
360
另一个例子就是io包下面的 io.ReadWriter ,它包含了io包下面的Reader和Writer两个interface:
361
+ ``` Go
349
362
350
363
// io.ReadWriter
351
364
type ReadWriter interface {
352
365
Reader
353
366
Writer
354
367
}
355
-
368
+ ```
356
369
### 反射
357
370
Go语言实现了反射,所谓反射就是能检查程序在运行时的状态。我们一般用到的包是reflect包。如何运用reflect包,官方的这篇文章详细的讲解了reflect包的实现原理,[ laws of reflection] ( http://golang.org/doc/articles/laws_of_reflection.html )
358
371
359
372
使用reflect一般分成三步,下面简要的讲解一下:要去反射是一个类型的值(这些值都实现了空interface),首先需要把它转化成reflect对象(reflect.Type或者reflect.Value,根据不同的情况调用不同的函数)。这两种获取方式如下:
373
+ ``` Go
360
374
361
375
t := reflect.TypeOf (i) // 得到类型的元数据,通过t我们能获取类型定义里面的所有元素
362
376
v := reflect.ValueOf (i) // 得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
363
-
377
+ ```
364
378
转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值,例如
379
+ ``` Go
365
380
366
381
tag := t.Elem ().Field (0 ).Tag // 获取定义在struct里面的标签
367
382
name := v.Elem ().Field (0 ).String () // 获取存储在第一个字段里面的值
368
-
383
+ ```
369
384
获取反射值能返回相应的类型和数值
385
+ ``` Go
370
386
371
387
var x float64 = 3.4
372
388
v := reflect.ValueOf (x)
373
389
fmt.Println (" type:" , v.Type ())
374
390
fmt.Println (" kind is float64:" , v.Kind () == reflect.Float64 )
375
391
fmt.Println (" value:" , v.Float ())
376
-
392
+ ```
377
393
最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理。反射的字段必须是可读写的意思是,如果下面这样写,那么会发生错误
394
+ ``` Go
378
395
379
396
var x float64 = 3.4
380
397
v := reflect.ValueOf (x)
381
398
v.SetFloat (7.1 )
382
-
399
+ ```
383
400
如果要修改相应的值,必须这样写
401
+ ``` Go
384
402
385
403
var x float64 = 3.4
386
404
p := reflect.ValueOf (&x)
387
405
v := p.Elem ()
388
406
v.SetFloat (7.1 )
389
-
407
+ ```
390
408
上面只是对反射的简单介绍,更深入的理解还需要自己在编程中不断的实践。
391
409
392
410
## links
0 commit comments