Creating an instance of a generic type in DART

Dart

Dart Problem Overview


I was wondering if is possible to create an instance of a generic type in Dart. In other languages like Java you could work around this using reflection, but I'm not sure if this is possible in Dart.

I have this class:

class GenericController <T extends RequestHandler> {

    void processRequest() {
        T t = new T();  // ERROR
    }
}

Dart Solutions


Solution 1 - Dart

I tried mezonis approach with the Activator and it works. But it is an expensive approach as it uses mirrors, which requires you to use "mirrorsUsed" if you don't want to have a 2-4MB js file.

This morning I had the idea to use a generic typedef as generator and thus get rid of reflection:

You define a method type like this: (Add params if necessary)

typedef S ItemCreator<S>();

or even better:

typedef ItemCreator<S> = S Function();

Then in the class that needs to create the new instances:

class PagedListData<T>{
  ...
  ItemCreator<T> creator;
  PagedListData(ItemCreator<T> this.creator) {

  }

  void performMagic() {
      T item = creator();
      ... 
  }
}

Then you can instantiate the PagedList like this:

PagedListData<UserListItem> users 
         = new PagedListData<UserListItem>(()=> new UserListItem());

You don't lose the advantage of using generic because at declaration time you need to provide the target class anyway, so defining the creator method doesn't hurt.

Solution 2 - Dart

You can use similar code:

import "dart:mirrors";

void main() {
  var controller = new GenericController<Foo>();
  controller.processRequest();
}

class GenericController<T extends RequestHandler> {
  void processRequest() {
    //T t = new T();
    T t = Activator.createInstance(T);
    t.tellAboutHimself();
  }
}

class Foo extends RequestHandler {
  void tellAboutHimself() {
    print("Hello, I am 'Foo'");
  }
}

abstract class RequestHandler {
  void tellAboutHimself();
}

class Activator {
  static createInstance(Type type, [Symbol constructor, List
      arguments, Map<Symbol, dynamic> namedArguments]) {
    if (type == null) {
      throw new ArgumentError("type: $type");
    }

    if (constructor == null) {
      constructor = const Symbol("");
    }

    if (arguments == null) {
      arguments = const [];
    }

    var typeMirror = reflectType(type);
    if (typeMirror is ClassMirror) {
      return typeMirror.newInstance(constructor, arguments, 
        namedArguments).reflectee;
    } else {
      throw new ArgumentError("Cannot create the instance of the type '$type'.");
    }
  }
}

Solution 3 - Dart

I don't know if this is still useful to anyone. But I have found an easy workaround. In the function you want to initialize the type T, pass an extra argument of type T Function(). This function should return an instance of T. Now whenever you want to create object of T, call the function.

class foo<T> {
    void foo(T Function() creator) {
        final t = creator();
        // use t
    }
}

P.S. inspired by Patrick's answer

Solution 4 - Dart

Here's my work around for this sad limitation

class RequestHandler {
  static final _constructors = {
    RequestHandler: () => RequestHandler(),
    RequestHandler2: () => RequestHandler2(),
  };
  static RequestHandler create(Type type) {
    return _constructors[type]();
  }
}

class RequestHandler2 extends RequestHandler {}

class GenericController<T extends RequestHandler> {
  void processRequest() {
    //T t = new T(); // ERROR
    T t = RequestHandler.create(T);
  }
}

test() {
  final controller = GenericController<RequestHandler2>();
  controller.processRequest();
}

Solution 5 - Dart

Sorry but as far as I know, a type parameter cannot be used to name a constructor in an instance creation expression in Dart.

Solution 6 - Dart

Working with FLutter

typedef S ItemCreator<S>();

mixin SharedExtension<T> {

    T getSPData(ItemCreator<T> creator) async {
        return creator();
    }
}

Abc a = sharedObj.getSPData(()=> Abc());

P.S. inspired by Patrick

Solution 7 - Dart

2022 answer

Just came across this problem and found out that although instantiating using T() is still not possible, you can get the constructor of an object easier with SomeClass.new in dart>=2.15.

So what you could do is:

MyClass<T> {
  final T Function() creator;
  MyClass(this.creator);

  T getGenericInstance() {
    return creator();
  }
}

and when using it:

final myClass = MyClass<SomeOtherClass>(SomeOtherClass.new)

Nothing different but looks cleaner imo.

Solution 8 - Dart

Inspired by Patrick's answer, this is the factory I ended up with.

class ServiceFactory<T> {
  static final Map<Type, dynamic> _cache = <String, dynamic>{};

  static T getInstance<T>(T Function() creator) {
    String typeName = T.toString();
    return _cache.putIfAbsent(typeName, () => creator());
  }
}

Then I would use it like this.

final authClient = ServiceFactory.getInstance<AuthenticationClient>(() => AuthenticationClient());

Warning: Erik made a very good point in the comment below that the same type name can exist in multiple packages and that will cause issues. As much as I dislike to force the user to pass in a string key (that way it's the consumer's responsibility to ensuring the uniqueness of the type name), that might be the only way.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionshuriquenView Question on Stackoverflow
Solution 1 - DartPatrick CornelissenView Answer on Stackoverflow
Solution 2 - DartmezoniView Answer on Stackoverflow
Solution 3 - DartHemilView Answer on Stackoverflow
Solution 4 - DartAnton DuzenkoView Answer on Stackoverflow
Solution 5 - DartyorodmView Answer on Stackoverflow
Solution 6 - DartTeenenggrView Answer on Stackoverflow
Solution 7 - DartAllen HuView Answer on Stackoverflow
Solution 8 - DartJames PouloseView Answer on Stackoverflow