前言
开始学习第十一章 接口与反射。
接口是什么
接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。
1
2
3
4
5
type Namer interface {
Method1 ( param_list ) return_type
Method2 ( param_list ) return_type
...
}
类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。
实现某个接口的类型(除了实现接口方法外)可以有其他的方法。
一个类型可以实现多个接口。
接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。
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
package main
import "fmt"
type Shaper interface {
Area () float64
}
type Square struct {
side float64
}
func ( sq * Square ) Area () float64 {
return sq . side * sq . side
}
func main (){
sq1 := new ( Square )
sq1 . side = 5
var areaIntf Shaper
areaIntf = sq1
//areaIntf := Shaper(sq1)
//areaIntf := sq1
fmt . Println ( areaIntf . Area ())
fmt . Println ( sq1 . Area ())
}
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
package main
import "fmt"
type Shaper interface {
Area () float64
}
type Square struct {
side float64
}
func ( sq * Square ) Area () float64 {
return sq . side * sq . side
}
type Rectangle struct {
length float64
width float64
}
func ( r * Rectangle ) Area () float64 {
return r . length * r . width
}
func main (){
sq := & Square { 5 }
r := & Rectangle { 2 , 1 }
shapers := [] Shaper { sq , r }
for _ , v := range shapers {
fmt . Println ( v )
fmt . Println ( v . Area ())
}
}
接口嵌套接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type ReadWrite interface {
Read ( b Buffer ) bool
Write ( b Buffer ) bool
}
type Lock interface {
Lock ()
Unlock ()
}
type File interface {
ReadWrite
Lock
Close ()
}
类型断言:如何检测和转换接口变量的类型
有一种方式来检测它的 动态 类型,即运行时在变量中存储的值的实际类型。
1
2
3
4
5
if v , ok := varI .( T ); ok { // checked type assertion
Process ( v )
return
}
// varI is not of type T
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import "fmt"
type Simpler interface {
Get () int
Set ( int )
}
type Simple struct {
n int
}
func ( s * Simple ) Get () int {
return s . n
}
func ( s * Simple ) Set ( n int ){
s . n = n
}
func main () {
var s Simpler = & Simple { 555 }
if s , ok := s .( * Simple ); ok {
fmt . Println ( s . Get ())
}
}
类型判断:type-switch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func classifier ( items ... interface {}) {
for i , x := range items {
switch x .( type ) {
case bool :
fmt . Printf ( "Param #%d is a bool\n" , i )
case float64 :
fmt . Printf ( "Param #%d is a float64\n" , i )
case int , int64 :
fmt . Printf ( "Param #%d is a int\n" , i )
case nil :
fmt . Printf ( "Param #%d is a nil\n" , i )
case string :
fmt . Printf ( "Param #%d is a string\n" , i )
default :
fmt . Printf ( "Param #%d is unknown\n" , i )
}
}
}
练习:
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
package main
import "fmt"
type Simpler interface {
Get () int
Put ( int )
}
type Simple struct {
i int
}
type RSimple struct {
i int
}
func ( p * Simple ) Get () int {
return p . i
}
func ( p * Simple ) Put ( u int ) {
p . i = u
}
func ( p * RSimple ) Get () int {
return p . i
}
func ( p * RSimple ) Put ( u int ) {
p . i = u
}
func fI ( it Simpler ) {
switch it .( type ) {
case * Simple :
fmt . Println ( "Simple" )
case * RSimple :
fmt . Println ( "RSimple" )
case nil :
fmt . Println ( "nil" )
default :
fmt . Println ( "unknown" )
}
}
func main () {
var s * Simple
fI ( s )
}
测试一个值是否实现了某个接口
1
2
3
4
5
6
7
type Stringer interface {
String () string
}
if sv , ok := v .( Stringer ); ok {
fmt . Printf ( "v implements String(): %s\n" , sv . String ()) // note: sv, not v
}
使用方法集与接口
在接口上调用方法时,必须有和方法定义时相同的接收者类型或者是可以从具体类型 P 直接可以辨识的:
Go 语言规范定义了接口方法集的调用规则:
类型 *T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
类型 T 的可调用方法集包含接受者为 T 的所有方法
类型 T 的可调用方法集不包含接受者为 *T 的方法
第一个例子:使用 Sorter 接口排序
1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import (
"fmt"
"learn1/src/sort"
)
func main () {
p := [] int { 1 , 35 , 2 , 111 , 67 , 32 , 6546 , 99 }
a := sort . IntArray ( p )
sort . Sort ( a )
fmt . Println ( a )
}
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
package sort
type Sorter interface {
Len () int
Less ( i , j int ) bool
Swap ( i , j int )
}
func Sort ( data Sorter ){
for j := 1 ; j < data . Len (); j ++ {
for i := 0 ; i < data . Len () - j ; i ++ {
if data . Less ( i + 1 , i ) {
data . Swap ( i , i + 1 )
}
}
}
}
type IntArray [] int
func ( p IntArray ) Len () int {
return len ( p )
}
func ( p IntArray ) Less ( i , j int ) bool {
return p [ i ] < p [ j ]
}
func ( p IntArray ) Swap ( i , j int ) {
p [ i ], p [ j ] = p [ j ], p [ i ]
}
空接口
空接口或者最小接口 不包含任何方法,它对实现不做任何要求
任何其他类型都实现了空接口。
可以给一个空接口类型的变量 var val interface {}
赋任何类型的值。
每个 interface {}
变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。
既然空接口可以赋任何类型的值,就可以构造出包含不同类型的数组:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import "fmt"
type Element interface {}
type Vector struct {
data [] Element
}
func ( v * Vector ) Get ( i int ) Element {
return v . data [ i ]
}
func ( v * Vector ) Set ( i int , e Element ) {
v . data [ i ] = e
}
func main () {
v := & Vector {[] Element { 1 , "hello" , true }}
fmt . Println ( v )
}
把数据切片复制给空接口切片必须一个一个显示复制:
1
2
3
4
5
var dataSlice [] myType = FuncReturnSlice ()
var interfaceSlice [] interface {} = make ([] interface {}, len ( dataSlice ))
for i , d := range dataSlice {
interfaceSlice [ i ] = d
}
反射包
reflect.TypeOf
和 reflect.ValueOf
,返回被检查对象的类型和值。
reflect.Type
和 reflect.Value
都有一个Kind()
方法返回一个常量来表示类型,而且它总是返回底层类型。
Value 有叫做 Int 和 Float 的方法可以获取存储在内部的值。
1
2
3
4
a := 12
v := reflect . ValueOf ( a ). Int ()
fmt . Println ( reflect . TypeOf ( v )) //int64
fmt . Println ( v ) //12
reflect.Value
对象的Interface()
方法得到还原(接口)值:
1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"fmt"
"reflect"
)
func main () {
a := 12
v := reflect . ValueOf ( a )
fmt . Println ( v . Interface ()) //12
}
通过反射修改(设置)值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import (
"fmt"
"reflect"
)
func main () {
a := 12
v := reflect . ValueOf ( a )
fmt . Println ( v . CanSet ()) //false
//v = v.Elem()
//fmt.Println(v.CanSet())
v = reflect . ValueOf ( & a )
fmt . Println ( v . CanSet ()) //false
v = v . Elem ()
fmt . Println ( v . CanSet ()) //true
v . SetInt ( 999 )
fmt . Println ( a ) //999
}
反射结构体
NumField()
方法返回结构体内的字段数量;通过一个 for 循环用索引取得每个字段的值 Field(i)
(返回的也是Value对象)。
同样能够调用签名在结构体上的方法,例如,使用索引 n 来调用:Method(n).Call(nil)
。
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
package main
import (
"fmt"
"reflect"
)
type Person struct {
Age int
Name string
}
func ( person * Person ) SayHello (){
fmt . Println ( "hello," + person . Name )
}
func main () {
person := Person { 18 , "feng" }
value := reflect . ValueOf ( person )
typ := reflect . TypeOf ( person )
fmt . Println ( value ) //{18 feng}
fmt . Println ( typ ) //main.Person
fmt . Println ( value . Kind ()) //struct
for i := 0 ; i < value . NumField (); i ++ {
fmt . Println ( value . Field ( i ))
//18
//feng
}
value = reflect . ValueOf ( & person )
fmt . Println ( value . NumMethod ()) //1
value . Method ( 0 ). Call ( nil ) //hello,feng
fmt . Println ( reflect . ValueOf ( & person ). Elem (). CanSet ()) //true
reflect . ValueOf ( & person ). Elem (). Field ( 1 ). SetString ( "ego" )
value . Method ( 0 ). Call ( nil ) //hello,ego
}
结构体中只有被导出字段(首字母大写)才是可设置的。且Golang没有提供类似Java
的setAccessible()
。