Interface and Reflection

@ Interface

Interface is a special kind of type in Go.

It is a blue print. If you want to build a vehicle , you must create a blue print before actually building it.

In Object Oriented programming languages , an interface is a specifications (blue prints / methods) of type (Vehicle).

An example of Vehicle interface :

type Vehicle interface {                // a blue print
    accelerate(speedlevel int)          // methods , a specification
}

where Vehicle is an interface type. Interface can be a variable's type

var v Vehicle

In Java , a type or class that implementation the interface needs to state it explicitly, and methods are implement inside the class.

public class Car implements Vehicle {
    ...
    public accelerate(speedlevel int){...}

}

However, in Go , such explicit declaration of implementation is not required , and the implementation of the methods are outside of the struct , here the example :

package main

import "fmt"

type Vehicle interface{       
    Accelerate(speedlevel int)              // methods , a specification
}

func Turbo(v Vehicle, s int){               // interface use as a function parameter (or return value) 
    v.Accelerate(s)
}

// --------------------------------
type Car struct{}

func (c Car) Accelerate(s int){             // Car struct with this method is implement Vehicle interface !
   fmt.Println(s, "car..o000")              // Name of the struct is not important, as long as it has method 
}                                           // Accelerate(speedlevel int) implemented ! [Duck Typing]

// --------------------------------
type Plane struct{}

func (p Plane) Accelerate(s int){            // Plane struct with this method is implement Vehicle interface !
   fmt.Println(s, "plane..o000")
}

// --------------------------------
func main() {

    car := new(Car)
    var v Vehicle
    v = car                                 // equivalent to v = Vehicle(car)
    v.Accelerate(1)

    plane := new(Plane)
    v = Vehicle(plane)
    v.Accelerate(2)

    vehicles := [...]Vehicle{car, plane}    // make it an array
    for i, _ := range vehicles {            // using a loop to print out
        vehicles[i].Accelerate(3)
    }

    Turbo(car,4)
    Turbo(plane,5)
}

[output]
1 car..o000
2 plane..o000
3 car..o000
3 plane..o000
4 car..o000
5 plane..o000

What is the benefit doing this instead of Java ? [TODO]

Embedded Types (Structs and Interfaces)

package main

import "fmt"

type Transformer struct{
     Vehicle                                // this is an embeded interface
     transform bool
}

// --------------------------------
type OptimusPrime struct{
     Plane                                  // this is a embeded struct
     transform bool
}

// --------------------------------
type Vehicle interface{                     // an interface
    Accelerate(speedlevel int)              // methods , a specification
}

// --------------------------------
type Car struct{}

func (c *Car) Accelerate(s int){            // implement Vehicle
   fmt.Println(s, "car..o000")              // the implementation has changed to pointer receiver
}

// --------------------------------
type Plane struct{}

func (p *Plane) Accelerate(s int){          // implement Vehicle
   fmt.Println(s, "plane..o000")            // the implementation has changed to pointer receiver
}

// --------------------------------
func main() {

    t := &Transformer{
        Vehicle : &Car{},                   // & added , otherwise will cause compile error !! why ? TODO
        transform : true,
        }
   // t.Vechicle.Accelerate(4)              // this will cause error : t.Vechicle undefined
                                            // (type *Transformer has no field or method Vechicle)
    t.Accelerate(5)

    o := &OptimusPrime{
        Plane : Plane{},
        transform : true,
        }
    o.Plane.Accelerate(6)                   // this is valid for struct !

    o.Accelerate(7)                         // “When we embed a type, the methods of that type become
                                            // methods of the outer type, but when they are invoked, 
                                            // the receiver of the method is the inner type, not the
                                            // outer one.” - Effective Go

}

[output]
5 car..o000
6 plane..o000
7 plane..o000

But if we insert one more function to the code above :

func (t *Transformer) Accelerate(s int){
     fmt.Println(s, "transformer..o000")
}

the result of the line,

t.Accelerate(5)                             // [output] 5 transformer..o000

will be "5 transformer..o000". See the different ? : ) . Yes , what happening here ? Is it some kind of magic ? or Golang style of OOP ? because l saw inheritance here - Interface can be inherited using embeded interface ! : )).

more example:

Function can use function's receiver to make call :

package main

import "fmt"

type Vehicle interface{
    Accelerate(speedlevel int)
}

type Transformer struct{
     v Vehicle                                    // an embeded interface in a field
}

func (t *Transformer) Accelerate(s int){
    fmt.Println(s, "transformer..o000") 
    t.v.Accelerate(s)                            // function can use function's receiver "t" to call !!
}

type OptimusPrime struct{}

func (o *OptimusPrime) Accelerate(s int){
    fmt.Println(s+1, "optimusPrime..o000") 
}

func main() {
    t := &Transformer{v: &OptimusPrime{}}
    t.Accelerate(5)
}

[output]
5 transformer..o000
6 optimusPrime..o000

Empty Interface ( Interface{} )

The interface{} type, the empty interface, is the interface that has no methods. Since there is no implements keyword, all types implement at least zero methods, and satisfying an interface is done automatically, all types satisfy the empty interface. That means that if you write a function that takes an interface{} value as a parameter, you can supply that function with any value.

The fmt.Printf function which accepts variable number of arguments of different types and produce a formatted output. If we take a look at the definition, it accepts empty interface (interface{}) as it signature. This means, Printf is using a mechanism based on empty interfaces to infer the types of values at run-time.

Duck Typing

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. - Heim, Michael

[TODO]

Ref :

  1. Why I like Go’s interfaces

@ Reflection

Examples:

a)

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Name    string `tag:"t1"`               // reflection will fail if the attributes is unexported !
    Email    string `tag:"t2"`
    Age       int  `tag:"t3"`
}

func (u *User) reflect() {
    val := reflect.ValueOf(u).Elem()

    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)
        typeField := val.Type().Field(i)
        tag := typeField.Tag

        fmt.Println(typeField.Name, valueField.Interface(), tag.Get("tag"))
    }
}

func main() {
    u := &User{
        Name:   "Moon",
        Email:  "[email protected]",
        Age:    45,
    }

    u.reflect()
}

[output]
Name Moon t1
Email [email protected] t2
Age 45 t3

b)

package main

import(
  "fmt"
  "reflect"
)

func main(){

  for name, t:= range attributes(&User{}) {
    fmt.Println(name, t.Name())
  }
}

type User struct {
  Id  int
  Name string
  Work func()
}

func attributes(m interface{}) (map[string]reflect.Type) {

  t:= reflect.TypeOf(m)
  if t.Kind() == reflect.Ptr{
    t= t.Elem()
  }

  attrs := make(map[string]reflect.Type)

  if t.Kind() != reflect.Struct {
    fmt.Println("%v type can't have attributes inspected\n", t.Kind())
    return attrs
  }

  for i := 0; i < t.NumField(); i++ {
    f := t.Field(i)
      if !f.Anonymous {
        attrs[f.Name] = f.Type
      }
     }

  return attrs
}

[output]
Work 
Id int
Name string

Relationship between interface and reflection

[TODO]

Further Reference:

  1. The Laws of Reflection

results matching ""

    No results matching ""