Playing with Reflections in Golang

What Are Reflections in Programming?

I went through many articles e.g. Wiki and Quora. These are very nice articles, but what I miss is essence of simplicity for a newbie programmer. So, I will try to explain this in really simple words and we will experiment with  Golang.

Let’s go point by point:

  • Reflection can help you to identify the type of an object at run time.
  • Yes, type can be printed as string.
  • Reflection can create an object from string. It means if I give you a configuration file with class names, you can configure your designs with that file by creating objects based on names.

A basic example of reflection, is finding function name from a windows DLL (GetProcAddress). This function helps us to find the function address from it’s name and we can call that address to execute that function.

// declare a type PGNSI
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);

//declare a variable of type PGNSI
   PGNSI pGNSI;
   SYSTEM_INFO si;

   
   // get address of funtion GetNativeSystemInfo and store in pGNSI
   pGNSI = (PGNSI) GetProcAddress(handle, "GetNativeSystemInfo");
   if(NULL != pGNSI)
   {
      // call function
      pGNSI(&si);
   }

don’t get confused above code in in C language just don’t try to run it, it’s just for explanation of concept.

Reflections in Golang

Golang also implements this beautiful feature of reflection. There is a sweet blog , Laws Of Reflection.

Type and Interface in Golang

Before diving into reflections, we need a little backgroung on types and interfaces. “Go is a statically typed language“, yes one type cannot be automatically typed in to another.

e.g.

package main
import "fmt"

func main() {

       type MyInt int
       var a int = 0
       var b MyInt = 10

       fmt.Println("Before")
       fmt.Println(a)
       fmt.Println(b)

       //a = b // .\main.go:18: cannot use b (type MyInt) as type int in assignment
       a = int(b) // good assignment

       fmt.Println("After")
       fmt.Println(a)
       fmt.Println(b)
}

Although MyInt and int are same kind of type, but we cannot assign one to another.

Interfaces in Go 

Interfaces are special kind of type, e.g.

// 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)
}

//an empty interface
type EmptyIface interface {

}
  • Every type in Golang implements empty interface.
  • A non empty interface consists of rule or method, any type abiding that rule becomes of interface type. e.g.
package main
import "fmt"

// Reader is the interface that wraps the basic Read method.
type Reader interface {
       Read(p []byte) (n int, err error)
}


type MyInt int

// MyInt implements Reader interface
func (a MyInt) Read(p []byte) (n int, err error) {
       return 10 ,nil
}

func main() {

       var b MyInt = 10

       p,_ := b.Read(nil)

       fmt.Println(p)

}

We don’t need type assertions for empty interface.

package main
import "fmt"


type Empty interface {

}

func main() {
       var data empty = 10
       fmt.Println(data)
}

As we are clear with types and interfaces let’s begin reflections.

Reflections

Go implements it’s reflection functionality in Package Reflection. The package consists of many routines and types let’s learn by using it.

reflect.TypeOf()

reflect.TypeOf() return a Type variable, which represents a Go Type. We can see this in output of the code below.

package main
import "fmt"
import "reflect"

type TestStruct struct {
       test int
}


type Inface interface {
       read()
}
func (k TestStruct) read() {

}

func main() {

       var integer int = 10
       var structure TestStruct
       var interfaceblank Inface
       var interfacew Inface  = TestStruct{}
       var interfacep Inface  = &TestStruct{}
       var myarray [6]int

       fmt.Println("=============================================================")
       var test reflect.Type =  reflect.TypeOf(integer)
       fmt.Println("output integer: ",test)
       fmt.Println("output integer kind: ",test.Kind())
       fmt.Println("=============================================================")


       test = reflect.TypeOf(structure)
       fmt.Println("output structure: ",test)
       fmt.Println("output structure kind: ",test.Kind())
       fmt.Println("=============================================================")

       test = reflect.TypeOf(interfaceblank)
       fmt.Println("output interfaceblank: ",test)
       //fmt.Println("output integer kind: ",test.Kind()) // panics for nil interface
       fmt.Println("=============================================================")

       test = reflect.TypeOf(interfacew)
       fmt.Println("output interfacew: ",test)
       fmt.Println("output interfacew kind: ",test.Kind())
       fmt.Println("=============================================================")

       test = reflect.TypeOf(interfacep)
       fmt.Println("output interfacep: ",test)
       fmt.Println("output interfacep kind: ",test.Kind())

       test = reflect.TypeOf(myarray)
       fmt.Println("output myarray: ",test)
       fmt.Println("output myarray kind: ",test.Kind())
       fmt.Println("output myarray elem: ",test.Elem())
}

=============================================================
output integer: int
output integer kind: int
=============================================================
output structure: main.TestStruct
output structure kind: struct
=============================================================
output interfaceblank: 
=============================================================
output interfacew: main.TestStruct
output interfacew kind: struct
=============================================================
output interfacep: *main.TestStruct
output interfacep kind: ptr
=============================================================
output myarray: [6]int
output myarray kind: array
output myarray elem: int

Type is the representation of a Go type.

A Kind represents the specific kind of type that a Type represents. The zero Kind is not a valid kind.

An Elem() will give type of element inside a Type. It panics if  type’s kind is not array, slice, channel, map or pointer.

Reflection A Live Code Example

 

package main

import (

       "fmt"

       "reflect"

)

type MyStruct struct {

       Name string `tag_name:"Name"`

       Wife  string `tag_name:"Wife"`

       Age       int    `tag_name:"Age"`

}

func (f * MyStruct) reflectIt() {

       // take out type inside *f

       val := reflect.ValueOf(f).Elem()

       // iterate over all the fields inside structure

       for i := 0; i < val.NumField(); i++ {
// read value of field

valueField := val.Field(i)

//read type of field

typeField := val.Type().Field(i)

// read tag of field

tag := typeField.Tag

fmt.Printf("Field Name: %s,\t Field Value: %v,\t Tag Value: %s\n", typeField.Name, valueField.Interface(), tag.Get("tag_name"))

}

}

func main() {

f := &MyStruct{Name: "Rajni",Wife:"Sakshi",Age:30,}

f.reflectIt()

}

output:
==============================================================================
Field Name: Name, Field Value: Rajni, Tag Value: Name
Field Name: Wife, Field Value: Sakshi, Tag Value: Wife
Field Name: Age, Field Value: 30, Tag Value: Age

The code above explains the reflection in few lines. Will be back with more topics. See you soon.

Happy Coding.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s