type MyInt int
var i int
var j MyInt
// Reader is the interface that wraps the basic Read method.
type Reader interface {
Read(p []byte) (n int, err error)
}
// Writer is the interface that wraps the basic Write method.
type Writer interface {
Write(p []byte) (n int, err error)
}
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on
interface{}
它表示了一个空的方法集,一切值都可以满足它,因为它们都有零值或方法。注:这里反射类型指 reflect.Type 和 reflect.Value。
从使用方法上来讲,反射提供了一种机制,允许程序在运行时检查接口变量内部存储的 (value, type) 对。package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 fmt.Println("type:", reflect.TypeOf(x)) }运行结果如下:
type: float64
大家可能会疑惑,为什么没看到接口?这段代码看起来只是把一个 float64 类型的变量 x 传递给 reflect.TypeOf 并没有传递接口。其实在 reflect.TypeOf 的函数签名里包含一个空接口:
// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 fmt.Println("value:", reflect.ValueOf(x)) }运行结果如下:
value: 3.4
类型 reflect.Type 和 reflect.Value 都有很多方法,我们可以检查和使用它们,这里我们举几个例子。package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float()) }运行结果如下:
type: float64
kind is float64: true
value: 3.4
package main import ( "fmt" "reflect" ) func main() { var x uint8 = 'x' v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) // uint8. fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true. x = uint8(v.Uint()) // v.Uint returns a uint64. }运行结果如下:
type: uint8
kind is uint8: true
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}
y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)
fmt.Println(v.Interface())
为什么不直接使用 fmt.Println(v)?因为 v 的类型是 reflect.Value,我们需要的是它的具体值,由于值的类型是 float64,我们也可以用浮点格式化打印它:fmt.Printf("value is %7.1e\n", v.Interface())
运行结果如下:3.4e+00
同样,这次也不需要对 v.Interface() 的结果进行类型断言,空接口值内部包含了具体值的类型信息,Printf 函数会恢复类型信息。
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic
panic: reflect: reflect.flag.mustBeAssignable using unaddressable value
这里问题不在于值7.1
不能被寻址,而是因为变量 v 是“不可写的”,“可写性”是反射类型变量的一个属性,但不是所有的反射类型变量都拥有这个属性。package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("settability of v:", v.CanSet()) }运行结果如下:
settability of v: false
对于一个不具有“可写性”的 Value 类型变量,调用 Set 方法会报出错误。
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
如果这行代码能够成功执行,它不会更新 x,虽然看起来变量 v 是根据 x 创建的,相反它会更新 x 存在于反射对象 v 内部的一个拷贝,而变量 x 本身完全不受影响。这会造成迷惑,并且没有任何意义,所以是不合法的。“可写性”就是为了避免这个问题而设计的。f(x)
代码中,我们把变量 x 的一个拷贝传递给函数,因此不期望它会改变 x 的值。如果期望函数 f 能够修改变量 x,我们必须传递 x 的地址(即指向 x 的指针)给函数 f,如下所示:f(&x)
反射的工作机制与此相同,如果想通过反射修改变量 x,就要把想要修改的变量的指针传递给反射库。package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 p := reflect.ValueOf(&x) // Note: take the address of x. fmt.Println("type of p:", p.Type()) fmt.Println("settability of p:", p.CanSet()) }运行结果如下:
type of p: *float64
settability of p: false
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 p := reflect.ValueOf(&x) // Note: take the address of x. v := p.Elem() fmt.Println("settability of v:", v.CanSet()) }运行结果如下:
settability of v: true
由于变量 v 代表 x, 因此我们可以使用 v.SetFloat 修改 x 的值:package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 p := reflect.ValueOf(&x) // Note: take the address of x. v := p.Elem() v.SetFloat(7.1) fmt.Println(v.Interface()) fmt.Println(x) }运行结果如下:
7.1
7.1
package main import ( "fmt" "reflect" ) func main() { type T struct { A int B string } t := T{23, "skidoo"} s := reflect.ValueOf(&t).Elem() typeOfT := s.Type() for i := 0; i < s.NumField(); i++ { f := s.Field(i) fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface()) } }运行结果如下:
0: A int = 23
1: B string = skidoo
package main import ( "fmt" "reflect" ) func main() { type T struct { A int B string } t := T{23, "skidoo"} s := reflect.ValueOf(&t).Elem() s.Field(0).SetInt(77) s.Field(1).SetString("Sunset Strip") fmt.Println("t is now", t) }运行结果如下:
t is now {77 Sunset Strip}
如果我们修改了程序让 s 由 t(而不是 &t)创建,程序就会在调用 SetInt 和 SetString 的地方失败,因为 t 的字段是不可设置的。
本文链接:http://task.lmcjl.com/news/15307.html