反射(Reflection)是探知数据自身结构的一种能力,不同的语言提供不同的反射机制,在 Go 语言中,反射的能力主要由reflect
包提供。
数据的 Type
在先前的文件中,有时会用到reflect.TypeOf()
来显示数据的类型名称,实际上,reflect.TypeOf()
返回Type
的实例,Type
是个接口定义,目前包含了以下的方法定义:
type Type interface {
Align() int
FieldAlign() int
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
Implements(u Type) bool
AssignableTo(u Type) bool
ConvertibleTo(u Type) bool
Comparable() bool
Bits() int
ChanDir() ChanDir
IsVariadic() bool
Elem() Type
Field(i int) StructField
FieldByIndex(index []int) StructField
FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool)
In(i int) Type
Key() Type
Len() int
NumField() int
NumIn() int
NumOut() int
Out(i int) Type
}
因此,你可以透过Type
的方法定义,获取某个类型的相关结构信息,举例来说:
package main
import (
"fmt"
"reflect"
)
type Account struct {
id string
name string
balance float64
}
func main() {
account := Account{"X123", "Justin Lin", 1000}
t := reflect.TypeOf(account)
fmt.Println(t.Kind()) // struct
fmt.Println(t.String()) // main.Account
/*
底下显示
id string
name string
balance float64
*/
for i, n := 0, t.NumField(); i < n; i++ {
f := t.Field(i)
fmt.Println(f.Name, f.Type)
}
}
如果reflect.TypeOf()
接受的是个指针,因为指针实际上只是个地址值,必须要透过Type
的Elem
方法获取指针的目标Type
,才能获取类型的相关成员:
package main
import (
"errors"
"fmt"
"reflect"
)
type Savings interface {
Deposit(amount float64) error
Withdraw(amount float64) error
}
type Account struct {
id string
name string
balance float64
}
func (ac *Account) Deposit(amount float64) error {
if amount <= 0 {
return errors.New("必须存入正数")
}
ac.balance += amount
return nil
}
func (ac *Account) Withdraw(amount float64) error {
if amount > ac.balance {
return errors.New("余额不足")
}
ac.balance -= amount
return nil
}
func main() {
var savings Savings = &Account{"X123", "Justin Lin", 1000}
t := reflect.TypeOf(savings)
for i, n := 0, t.NumMethod(); i < n; i++ {
f := t.Method(i)
fmt.Println(f.Name, f.Type)
}
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
fmt.Println(t.Kind())
fmt.Println(t.String())
for i, n := 0, t.NumField(); i < n; i++ {
f := t.Field(i)
fmt.Println(f.Name, f.Type)
}
}
有上面的范例中,也示范了如何获取接口定义的方法信息,这个范例会显示以下的结果:
Deposit func(*main.Account, float64) error
Withdraw func(*main.Account, float64) error
struct
main.Account
id string
name string
数据的 Kind
上面的范例中,使用了Type
的Kind()
方法,这会返回Kind
枚举值:
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
以下是个简单的类型测试:
package main
import (
"fmt"
"reflect"
)
type Duck struct {
name string
}
func main() {
values := [...](interface{}){
Duck{"Justin"},
Duck{"Monica"},
[...]int{1, 2, 3, 4, 5},
map[string]int{"caterpillar": 123456, "monica": 54321},
10,
}
for _, value := range values {
switch t := reflect.TypeOf(value); t.Kind() {
case reflect.Struct:
fmt.Println("it's a struct.")
case reflect.Array:
fmt.Println("it's a array.")
case reflect.Map:
fmt.Println("it's a map.")
case reflect.Int:
fmt.Println("it's a integer.")
default:
fmt.Println("非预期之类型")
}
}
}
数据的 Value
如果想实际获得数据的值,可以使用reflect.ValueOf()
函数,这会返回Value
实例,Value
是个结构,定义了一些方法可以使用,可用来获取实际的值,例如:
package main
import (
"fmt"
"reflect"
)
type Account struct {
id string
name string
balance float64
}
func main() {
x := 10
vx := reflect.ValueOf(x)
fmt.Printf("x = %d\n", vx.Int())
account := Account{"X123", "Justin Lin", 1000}
vacct := reflect.ValueOf(account)
fmt.Printf("id = %s\n", vacct.FieldByName("id").String())
fmt.Printf("name = %s\n", vacct.FieldByName("name").String())
fmt.Printf("balance = %.2f\n", vacct.FieldByName("balance").Float())
}
如果是个指针,一样也是要透过Elem()
方法获取目标值,例如:
package main
import (
"fmt"
"reflect"
)
type Account struct {
id string
name string
balance float64
}
func main() {
x := 10
vx := reflect.ValueOf(&x)
fmt.Printf("x = %d\n", vx.Elem().Int())
account := &Account{"X123", "Justin Lin", 1000}
vacct := reflect.ValueOf(account).Elem()
fmt.Printf("id = %s\n", vacct.FieldByName("id").String())
fmt.Printf("name = %s\n", vacct.FieldByName("name").String())
fmt.Printf("balance = %.2f\n", vacct.FieldByName("balance").Float())
}
可以透过Value
对值进行变动,不过,Value
必须是可定址的,具体来说,就是reflect.ValueOf()
必须接受指针:
package main
import (
"fmt"
"reflect"
)
type Account struct {
id string
name string
balance float64
}
func main() {
x := 10
vx := reflect.ValueOf(&x).Elem()
fmt.Printf("x = %d\n", vx.Int()) // x = 10
vx.SetInt(20)
fmt.Printf("x = %d\n", x) // x = 20
}
上面的例子若改成以下,就会出现错误:
package main
import (
"fmt"
"reflect"
)
type Account struct {
id string
name string
balance float64
}
func main() {
x := 10
vx := reflect.ValueOf(x)
fmt.Printf("x = %d\n", vx.Int())
vx.SetInt(20) // panic: reflect: reflect.Value.SetInt using unaddressable value
fmt.Printf("x = %d\n", x)
}
技术上来说,上面的例子,只是传了x
的值复本给reflect.ValueOf()
,因此,对其设值并无意义。
若对反射想进一步研究,可以参考〈The Laws of Reflection〉。