The reflect Package

This lesson will help you explore the reflect package more relative to interfaces.

Methods and types in reflect

In the previous chapter, we saw how reflect can be used to analyze a struct. Here, we elaborate further on its powerful possibilities. Reflection in computing is the ability of a program to examine its structure, mainly through the types; it’s a form of meta-programming. The reflect can be used to investigate types and variables at runtime, e.g., their size, and methods, and it can also call these methods dynamically. It can also be useful to work with types from packages of which you do not have the source. It’s a powerful tool that should be used with care and avoided unless strictly necessary.

Basic information of a variable is its type and its value: these are represented in the reflection package by the types Type, which represents a general Go type, and Value, which is the reflection interface to a Go value. Two simple functions, reflect.TypeOf and reflect.ValueOf, retrieve the Type and Value pieces out of any value. For example, if x is defined as:

var x float64 = 3.4

Then, reflect.TypeOf(x) gives float64 and reflect.ValueOf(x) returns 3.4.

Reflection works by examining an interface value; the variable is first converted to the empty interface. This becomes apparent if you look at the signatures of both of these functions:

func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value

The interface value then contains a type and value pair.

Reflection goes from interface values to reflection objects and back again, as we will see. Both reflect.Type and reflect.Value have lots of methods that let us examine and manipulate them. One important example is that Value has a Type method that returns the Type of a reflect.Value. Another is that both Type and Value have a Kind method that returns a constant, indicating what sort of item is stored: Uint, Float64, Slice, and so on. Also, methods on Value with names like Int and Float let us grab the values (like int64 and float64) stored inside. The different kinds of Type are defined as constants:

type Kind uint8
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
)

For our variable x, if v := reflect.ValueOf(x) then v.Kind() is float64, so the following is true:

v.Kind() == reflect.Float64

The Kind is always the underlying type: if you define:

type MyInt int
var m MyInt = 5
v := reflect.ValueOf(m)

Then, v.Kind() returns reflect.Int.

The Interface() method on a Value recovers the (interface) value, so to print a Value v do this:

fmt.Println(v.Interface())

Experiment with these possibilities with the following code:

Get hands-on with 1200+ tech skills courses.