go - How to change method implementation in run-time using reflection?


Keywords:reflection 


Question: 

I have this type implementation:

type A struct{
  name string
}

func (a A) getName() string {
  return "My name is " + a.name 
}

How I can change implementation of method getName() for this type using reflection? For example, I want use next implementation instead of current:

func newGetName() string {
  return "test reflection"
} 

2 Answers: 

Go is a compiled language. As such, it's not possible to modify the implementation of things at runtime. What you can do is changing the place function pointers point to:

var getName func(string) = func(name string) {
    return "my name is " + name
}

In order to make this work with a structure, you have to resort to a few tricks. First add getName as a member to A:

type A struct {
    name string
    getName func() string
}

Then we enclose a pointer to the structure as an implicit (i.e. closed over) parameter:

foo := &A{name: "Hans"}
foo.getName = func() string {
    return "my name is " + name
}

Now you can call A.getName() and the result is "my name is hans". You can use method expressions and many other features just fine, but getName is a structure member and not a method of A, so keep this in mind. When you want to give a new meaning to getName, assign something different to it:

foo.getName = func() string {
    return "test reflection"
}

Another idea that is especially applicable if you know in advance what implementations getName could have is to add a new member to A that says what implementation getName currently has and then switch over this variable.

 

Note the idiomatic Go is to not do that and use interface instead:

See this example:

package main

import "fmt"

type Aer interface {
    getName() string
}

type A struct {
    name string
}

func (a A) getName() string {
    return "My name is " + a.name
}

type testA struct {
    a A
}

func (ta testA) getName() string {
    return "NEW: My name is " + ta.a.name
}
func main() {
    a := A{name: "nameA"}
    fmt.Println(a.getName())
    ta := testA{a: a}
    fmt.Println(ta.getName())
}

Output:

My name is nameA
NEW: My name is nameA