reflect
standard package.
fmt
and encoding
packages, heavily rely on the reflection functionalities.
Type
and Value
types defined in the reflect
standard package. The remaining of this article will show some examples on how to use values of the two types.
reflect.Type
Type and Values
reflect.Type
value from an arbitrary non-interface value by calling the reflect.TypeOf
function. The result reflect.Type
value represents the type of the non-interface value. Surely, we can also pass an interface value to a reflect.TypeOf
function call, but the call will return a reflect.Type
value which represents the dynamic type of the interface value. In fact, the reflect.TypeOf
function has only one parameter of type interface{}
and always returns a reflect.Type
value which represents the dynamic type of the only interface parameter. To get a reflect.Type
value which represents an interface type by using the reflect.TypeOf
function, we must use indirect ways which will be introduced below to achieve this goal.
reflect.TypeFor
function to get a reflect.Type
value which represents a type which is known at compile time. The compile-time known type may be either non-interface or interface.
reflect.Type
type is an interface type. It specifies several methods. We can call these methods to inspect the information of the type represented by a reflect.Type
receiver value. Some of these methods apply for all kinds of types, some of them are one kind or several kinds specific. Please read the documentation of each method for details. Calling one of the methods through an improper reflect.Type
receiver value will produce a panic.
package main
import "fmt"
import "reflect"
func main() {
type A = [16]int16
var c <-chan map[A][]byte
tc := reflect.TypeOf(c)
fmt.Println(tc.Kind()) // chan
fmt.Println(tc.ChanDir()) // <-chan
tm := tc.Elem()
ta, tb := tm.Key(), tm.Elem()
// The next line prints: map array slice
fmt.Println(tm.Kind(), ta.Kind(), tb.Kind())
tx, ty := ta.Elem(), tb.Elem()
// byte is an alias of uint8
fmt.Println(tx.Kind(), ty.Kind()) // int16 uint8
fmt.Println(tx.Bits(), ty.Bits()) // 16 8
fmt.Println(tx.ConvertibleTo(ty)) // true
fmt.Println(tb.ConvertibleTo(ta)) // false
// Slice and map types are incomparable.
fmt.Println(tb.Comparable()) // false
fmt.Println(tm.Comparable()) // false
fmt.Println(ta.Comparable()) // true
fmt.Println(tc.Comparable()) // true
}
Elem
to get the element types of some container types (a channel type, a map type, a slice type and an array type). In fact, we can also use this method to get the base type of a pointer type. For example,
package main
import "fmt"
import "reflect"
type T []interface{m()}
func (T) m() {}
func main() {
tp := reflect.TypeOf(new(interface{}))
tt := reflect.TypeOf(T{})
fmt.Println(tp.Kind(), tt.Kind()) // ptr slice
// Get two interface Types indirectly.
ti, tim := tp.Elem(), tt.Elem()
// The next line prints: interface interface
fmt.Println(ti.Kind(), tim.Kind())
fmt.Println(tt.Implements(tim)) // true
fmt.Println(tp.Implements(tim)) // false
fmt.Println(tim.Implements(tim)) // true
// All types implement any blank interface type.
fmt.Println(tp.Implements(ti)) // true
fmt.Println(tt.Implements(ti)) // true
fmt.Println(tim.Implements(ti)) // true
fmt.Println(ti.Implements(ti)) // true
}
reflect.Type
value which represents an interface type.
package main
import "fmt"
import "reflect"
type F func(string, int) bool
func (f F) m(s string) bool {
return f(s, 32)
}
func (f F) M() {}
type I interface{m(s string) bool; M()}
func main() {
var x struct {
F F
i I
}
tx := reflect.TypeOf(x)
fmt.Println(tx.Kind()) // struct
fmt.Println(tx.NumField()) // 2
fmt.Println(tx.Field(1).Name) // i
// Package path is an intrinsic property of
// non-exported selectors (fields or methods).
fmt.Println(tx.Field(0).PkgPath) //
fmt.Println(tx.Field(1).PkgPath) // main
tf, ti := tx.Field(0).Type, tx.Field(1).Type
fmt.Println(tf.Kind()) // func
fmt.Println(tf.IsVariadic()) // false
fmt.Println(tf.NumIn(), tf.NumOut()) // 2 1
t0, t1, t2 := tf.In(0), tf.In(1), tf.Out(0)
// The next line prints: string int bool
fmt.Println(t0.Kind(), t1.Kind(), t2.Kind())
fmt.Println(tf.NumMethod(), ti.NumMethod()) // 1 2
fmt.Println(tf.Method(0).Name) // M
fmt.Println(ti.Method(1).Name) // m
_, ok1 := tf.MethodByName("m")
_, ok2 := ti.MethodByName("m")
fmt.Println(ok1, ok2) // false true
}
reflect.Type.NumMethod
only returns the number of exported methods (including implicitly declared ones) of a type. We are unable to get the information of a non-exported method by using the reflect.Type.MethodByName
method. For interface types, the limits don't exist (the fact was not mentioned in the docs of the two methods before Go 1.16). Such situation also applies to the corresponding methods of the reflect.Value
type introduced in the next section.
reflect.Type.NumField
method call returns the number of all fields (including non-exported ones) of a struct type, it is not a good idea to use the reflect.Type.FieldByName
method to get the information of a non-exported field.
reflect.StructTag
, which has two methods, Get
and Lookup
, to inspect the key-value pairs specified in field tags. An example of inspecting struct field tags:
package main
import "fmt"
import "reflect"
type T struct {
X int `max:"99" min:"0" default:"0"`
Y, Z bool `optional:"yes"`
}
func main() {
t := reflect.TypeOf(T{})
x := t.Field(0).Tag
y := t.Field(1).Tag
z := t.Field(2).Tag
fmt.Println(reflect.TypeOf(x)) // reflect.StructTag
// v is a string
v, present := x.Lookup("max")
fmt.Println(len(v), present) // 2 true
fmt.Println(x.Get("max")) // 99
fmt.Println(x.Lookup("optional")) // false
fmt.Println(y.Lookup("optional")) // yes true
fmt.Println(z.Lookup("optional")) // yes true
}
`optional: "yes"`
doesn't form key-value pairs.
`json:"author, omitempty"`
,`json:" author,omitempty"`
and`json:"author,omitempty"`
are different from each other.
reflect.TypeOf
function, we can also use some other functions in the reflect
standard package to create reflect.Type
values which represent some unnamed composite types.
package main
import "fmt"
import "reflect"
func main() {
ta := reflect.ArrayOf(5, reflect.TypeOf(123))
fmt.Println(ta) // [5]int
tc := reflect.ChanOf(reflect.SendDir, ta)
fmt.Println(tc) // chan<- [5]int
tp := reflect.PtrTo(ta)
fmt.Println(tp) // *[5]int
ts := reflect.SliceOf(tp)
fmt.Println(ts) // []*[5]int
tm := reflect.MapOf(ta, tc)
fmt.Println(tm) // map[[5]int]chan<- [5]int
tf := reflect.FuncOf([]reflect.Type{ta},
[]reflect.Type{tp, tc}, false)
fmt.Println(tf) // func([5]int) (*[5]int, chan<- [5]int)
tt := reflect.StructOf([]reflect.StructField{
{Name: "Age", Type: reflect.TypeOf("abc")},
})
fmt.Println(tt) // struct { Age string }
fmt.Println(tt.NumField()) // 1
}
reflect.Type
methods which are not used in above examples, please read the reflect
package documentation for their usages.
reflect.Value
Type and Values
reflect.Value
value from an arbitrary non-interface value by calling the reflect.ValueOf
function. The result reflect.Value
value represents the non-interface value. Same as the reflect.TypeOf
function, the reflect.ValueOf
function also has only one parameter of type interface{}
. When an interface argument is passed to a reflect.ValueOf
function call, the call will return a reflect.Value
value which represents the dynamic value of the interface argument. To get a reflect.Value
value which represents an interface value, we must use indirect ways which will be introduced below to achieve this goal.
reflect.Value
value v
is often called the underlying value of v
.
reflect.Value
type. We can call these methods to inspect the information of (and manipulate) the underlying value of a reflect.Value
receiver value. Some of these methods apply for all kinds of values, some of them are one kind or several kinds specific. Please read the reflect
standard package documentation for details. Calling a kind-specific method with an improper reflect.Value
receiver value will produce a panic.
CanSet
method of a reflect.Value
value returns whether or not the underlying value of the reflect.Value
value is modifiable (can be assigned to). If the Go value is modifiable, we can call the Set
method of the corresponding reflect.Value
value to modify the Go value. Note, the reflect.Value
values returned directly by reflect.ValueOf
function calls are always read-only.
package main
import "fmt"
import "reflect"
func main() {
n := 123
p := &n
vp := reflect.ValueOf(p)
fmt.Println(vp.CanSet(), vp.CanAddr()) // false false
vn := vp.Elem() // get the value referenced by vp
fmt.Println(vn.CanSet(), vn.CanAddr()) // true true
vn.Set(reflect.ValueOf(789)) // <=> vn.SetInt(789)
fmt.Println(n) // 789
}
package main
import "fmt"
import "reflect"
func main() {
var s struct {
X interface{} // an exported field
y interface{} // a non-exported field
}
vp := reflect.ValueOf(&s)
// If vp represents a pointer. the following
// line is equivalent to "vs := vp.Elem()".
vs := reflect.Indirect(vp)
// vx and vy both represent interface values.
vx, vy := vs.Field(0), vs.Field(1)
fmt.Println(vx.CanSet(), vx.CanAddr()) // true true
// vy is addressable but not modifiable.
fmt.Println(vy.CanSet(), vy.CanAddr()) // false true
vb := reflect.ValueOf(123)
vx.Set(vb) // okay, for vx is modifiable
// vy.Set(vb) // will panic, for vy is unmodifiable
fmt.Println(s) // {123 <nil>}
fmt.Println(vx.IsNil(), vy.IsNil()) // false true
}
reflect.Value
value whose underlying value is referenced by the underlying value (a pointer value) of another reflect.Value
value.
Elem
method of a reflect.Value
value which represents the pointer value.
reflect.Value
value which represents the pointer value to a reflect.Indirect
function call. (If the argument passed to a reflect.Indirect
function call doesn't represent a pointer value, then the call returns a copy of the argument.)
reflect.Value.Elem
method can be also used to get a reflect.Value
value which represents the dynamic value of an interface value. For example,
package main
import "fmt"
import "reflect"
func main() {
var z = 123
var y = &z
var x interface{} = y
v := reflect.ValueOf(&x)
vx := v.Elem()
vy := vx.Elem()
vz := vy.Elem()
vz.Set(reflect.ValueOf(789))
fmt.Println(z) // 789
}
reflect
standard package also declares some reflect.Value
related functions. Each of these functions corresponds to a built-in function or a non-reflection functionality, The following example demonstrates how to bind a (kind of) custom generic function to different function values.
package main
import "fmt"
import "reflect"
func InvertSlice(args []reflect.Value) []reflect.Value {
inSlice, n := args[0], args[0].Len()
outSlice := reflect.MakeSlice(inSlice.Type(), 0, n)
for i := n-1; i >= 0; i-- {
element := inSlice.Index(i)
outSlice = reflect.Append(outSlice, element)
}
return []reflect.Value{outSlice}
}
func Bind(p interface{},
f func ([]reflect.Value) []reflect.Value) {
// invert represents a function value.
invert := reflect.ValueOf(p).Elem()
invert.Set(reflect.MakeFunc(invert.Type(), f))
}
func main() {
var invertInts func([]int) []int
Bind(&invertInts, InvertSlice)
fmt.Println(invertInts([]int{2, 3, 5})) // [5 3 2]
var invertStrs func([]string) []string
Bind(&invertStrs, InvertSlice)
fmt.Println(invertStrs([]string{"Go", "C"})) // [C Go]
}
reflect.Value
is a function value, then we can call the Call
method of the reflect.Value
to call the underlying function.
package main
import "fmt"
import "reflect"
type T struct {
A, b int
}
func (t T) AddSubThenScale(n int) (int, int) {
return n * (t.A + t.b), n * (t.A - t.b)
}
func main() {
t := T{5, 2}
vt := reflect.ValueOf(t)
vm := vt.MethodByName("AddSubThenScale")
results := vm.Call([]reflect.Value{reflect.ValueOf(3)})
fmt.Println(results[0].Int(), results[1].Int()) // 21 9
neg := func(x int) int {
return -x
}
vf := reflect.ValueOf(neg)
fmt.Println(vf.Call(results[:1])[0].Int()) // -21
fmt.Println(vf.Call([]reflect.Value{
vt.FieldByName("A"), // panic on changing to "b"
})[0].Int()) // -5
}
vt.FieldByName("A")
in the above example is replaced with vt.FieldByName("b")
, a panic will occur.
package main
import "fmt"
import "reflect"
func main() {
valueOf := reflect.ValueOf
m := map[string]int{"Unix": 1973, "Windows": 1985}
v := valueOf(m)
// A zero second Value argument means to delete an entry.
v.SetMapIndex(valueOf("Windows"), reflect.Value{})
v.SetMapIndex(valueOf("Linux"), valueOf(1991))
for i := v.MapRange(); i.Next(); {
fmt.Println(i.Key(), "\t:", i.Value())
}
}
MapRange
method is supported since Go 1.12.
package main
import "fmt"
import "reflect"
func main() {
c := make(chan string, 2)
vc := reflect.ValueOf(c)
vc.Send(reflect.ValueOf("C"))
succeeded := vc.TrySend(reflect.ValueOf("Go"))
fmt.Println(succeeded) // true
succeeded = vc.TrySend(reflect.ValueOf("C++"))
fmt.Println(succeeded) // false
fmt.Println(vc.Len(), vc.Cap()) // 2 2
vs, succeeded := vc.TryRecv()
fmt.Println(vs.String(), succeeded) // C true
vs, sentBeforeClosed := vc.Recv()
fmt.Println(vs.String(), sentBeforeClosed) // Go true
vs, succeeded = vc.TryRecv()
fmt.Println(vs.String()) // <invalid Value>
fmt.Println(succeeded) // false
}
reflect.Select
function to simulate a select
code block with dynamic number of case
branches at run time.
package main
import "fmt"
import "reflect"
func main() {
c := make(chan int, 1)
vc := reflect.ValueOf(c)
succeeded := vc.TrySend(reflect.ValueOf(123))
fmt.Println(succeeded, vc.Len(), vc.Cap()) // true 1 1
vSend, vZero := reflect.ValueOf(789), reflect.Value{}
branches := []reflect.SelectCase{
{Dir: reflect.SelectDefault, Chan: vZero, Send: vZero},
{Dir: reflect.SelectRecv, Chan: vc, Send: vZero},
{Dir: reflect.SelectSend, Chan: vc, Send: vSend},
}
selIndex, vRecv, sentBeforeClosed := reflect.Select(branches)
fmt.Println(selIndex) // 1
fmt.Println(sentBeforeClosed) // true
fmt.Println(vRecv.Int()) // 123
vc.Close()
// Remove the send case branch this time,
// for it may cause panic.
selIndex, _, sentBeforeClosed = reflect.Select(branches[:2])
fmt.Println(selIndex, sentBeforeClosed) // 1 false
}
reflect.Value
values may be nothing. For example, zero reflect.Value
values.
package main
import "reflect"
import "fmt"
func main() {
var z reflect.Value // a zero Value value
fmt.Println(z) // <invalid reflect.Value>
v := reflect.ValueOf((*int)(nil)).Elem()
fmt.Println(v) // <invalid reflect.Value>
fmt.Println(v == z) // true
var i = reflect.ValueOf([]interface{}{nil}).Index(0)
fmt.Println(i) // <nil>
fmt.Println(i.Elem() == z) // true
fmt.Println(i.Elem()) // <invalid reflect.Value>
}
reflect.ValueOf
function to create a reflect.Value
value representing the Go value, through the help of interface{}
. The inverse process in similar, we can call the Interface
method of a reflect.Value
value to get an interface{}
value, then type assert on the interface{}
value to get the Go value represented by (a.k.a., the underlying value of ) the reflect.Value
value. But please note that, calling the Interface
method of a reflect.Value
value which represents a non-exported field causes a panic.
package main
import (
"fmt"
"reflect"
"time"
)
func main() {
vx := reflect.ValueOf(123)
vy := reflect.ValueOf("abc")
vz := reflect.ValueOf([]bool{false, true})
vt := reflect.ValueOf(time.Time{})
x := vx.Interface().(int)
y := vy.Interface().(string)
z := vz.Interface().([]bool)
m := vt.MethodByName("IsZero").Interface().(func() bool)
fmt.Println(x, y, z, m()) // 123 abc [false true] true
type T struct {x int}
t := &T{3}
v := reflect.ValueOf(t).Elem().Field(0)
fmt.Println(v) // 3
fmt.Println(v.Interface()) // panic
}
reflect.Value.IsZero
was introduced in Go 1.13. It is used to check whether or not the underlying value of a reflect.Value
value is a zero value.
reflect.Value.CanConvert(T reflect.Type)
introduced in Go 1.17 is used to check whether or not a conversion will success.
CanConvert
method:
package main
import (
"fmt"
"reflect"
)
func main() {
s := reflect.ValueOf([]int{1, 2, 3, 4, 5})
ts := s.Type()
t1 := reflect.TypeOf(&[5]int{})
t2 := reflect.TypeOf(&[6]int{})
fmt.Println(ts.ConvertibleTo(t1)) // true
fmt.Println(ts.ConvertibleTo(t2)) // true
fmt.Println(s.CanConvert(t1)) // true
fmt.Println(s.CanConvert(t2)) // false
}
reflect.Value
related functions and methods which are not used in above examples, please read the reflect
package documentation for their usages. In addition, please note that there are some reflection related details mentioned in Go details 101.
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.