Go dynamically instantiating struct by name at runtime via reflection

1.6k Views Asked by At

Is there anything equivalent in Go to the dynamic Class instantiation capabilities provided by languages like Java (note: requisite exception handling logic has been omitted here for the sake of brevity):

Class cls = Class.forName("org.company.domain.User");
Constructor<User> userConstructor = cls.getConstructor();
User user1 = userConstructor.newInstance();

The short Java snippet above essentially grabs reference to the Class via the supplied fully qualified classpath string, the class reference is then used to obtain reference to a zero-argument constructor (where one exists) and finally the constructor is used to obtain reference to an instance of the class.

I've yet to find an example of a similar mechanism in Go that may achieve similar results. More specifically, it would seem that the reflect package in go requires that the caller already have reference to the type of struct they wish to instantiate. The standard idiom in this regard seems to be as follows:

reflect.New(reflect.TypeOf(domain.User))

Note: The argument provided to the reflect.TypeOf function MUST be a Type not a string. Can a struct be instantiated in Go, via the reflect package, using nothing more than its fully qualified name?

1

There are 1 best solutions below

8
On BEST ANSWER

Kubernetes handles this exact process in the runtime.Scheme structure. The idea is that you register types with names or some other identifier, then you can ask for new instances of those types at will based on the identifier. Generally speaking this identifier is derived during a serialization process for example, rather then hard-coded into the source.

The catch is as you said, you need to create a new instance initially. While this pattern is un-common, I have come across two cases in my professional career where this was the logical solution. Here is an example of a very stripped down version of what the K8s runtime.Scheme does to accomplish this and it may work for what you're trying to do and here it is in action:

package main

import (
    "fmt"
    "reflect"
)

type Scheme struct {
    types map[string]reflect.Type
}

func (s *Scheme) RegisterType(name string, t interface{}) {
    a := reflect.TypeOf(t)
    s.types[name] = a
}

func (s *Scheme) New(name string) (interface{}, error) {
    t, ok := s.types[name]
    if !ok {
        return nil, fmt.Errorf("unrecognized type name: %s", name)
    }
    return reflect.New(t).Interface(), nil
}

func NewScheme() *Scheme {
    return &Scheme{types: map[string]reflect.Type{}}
}

type MyType struct {
    Foo string
}

func main() {
    scheme := NewScheme()
    scheme.RegisterType("my.type", MyType{})
    myType, _ := scheme.New("my.type")
    myType.(*MyType).Foo = "bar"
    fmt.Println("%+v", myType)
}