Source:
(1)The Laws of Reflection
Digest:
Reflection in computing is the ability of a program to examine its own structure, particularly through types; it's a form of metaprogramming.
1. Types and interfaces:
Go is statically typed. Every variable has a static type, that is, exactly one type known and fixed at compile time: int, float32, *MyType, []byte, and so on. If we declare
type MyInt int
var i int
var j MyInt
then i has type int and j has type MyInt. The variables i and j have distinct static types and, although they have the same underlying type, they cannot be assigned to one another without a conversion.
One important category of type is interface types, which represent fixed sets of methods. An interface variable can store any concrete (non-interface) value as long as that value implements the interface's methods.
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on
It's important to be clear that whatever concrete value r may hold, r's type is always io.Reader: Go is statically typed and the static type of r is io.Reader.
Some people say that Go's interfaces are dynamically typed, but that is misleading. They are statically typed: a variable of interface type always has the same static type, and even though at run time the value stored in the interface variable may change type, that value will always satisfy the interface.
We need to be precise about all this because reflection and interfaces are closely related.
2. The representation of an interface:
A variable of interface type stores a pair: the concrete value assigned to the variable, and that value's type descriptor. To be more precise, the value is the underlying concrete data item that implements the interface and the type describes the full type of that item.
The static type of the interface determines what methods may be invoked with an interface variable, even though the concrete value inside may have a larger set of methods.
One important detail is that the pair inside an interface always has the form (value,concrete type) and cannot have the form (value,interface type). Interfaces do not hold interface values.
The first law of reflection – 1. Reflection goes from interface value to reflection object.
At the basic level, reflection is just mechanism to examine the type and value pair stored inside an interface variable.
The reflection library has a couple of properties worth singling out. First, to keep the API simple, the "getter" and "setter" methods of Value operate on the largest type that can hold the value: int64 for all signed integers, for instance.
The second property is that the Kind of a reflection object describes the underlying type, not the static type.
The second law of reflection – 2. Reflection goes from reflection object to interface value.
Like physical reflection, reflection in Go generates its own inverse.
Given a reflect.Value we can recover an interface value using the Interface method; in effect the method packs the type and value information back into an interface representation and returns the result.
In short, the Interface method is the inverse of the ValueOf function, except that its result is always of static type interface{}.
Reiterating: Reflection goes from interface values to reflection objects and back again.
The third law of reflection – 3. To modify a reflection object, the value must be settable.
What is settability? Settability is a bit like addressability, but stricter. It's the property that a reflection object can modify the actual storage that was used to create the reflection object. Settability is determined by whether the reflection object holds the original item.
Reflection can be hard to understand but it's doing exactly what the language does, albeit through reflection Types and Values that can disguise what's going on. Just keep in mind that reflection Values need the address of something in order to modify what they represent.
3. Structs:
As long as we have the address of the structure, we can modify its fields.
Only exported fields of a struct are settable.