Dart template class inheriting from the template parameter

Issue

I would like to perform this in Dart:

abstract class A
{
  int f();
}

class A1 extends A
{
  @override
  int f() => 1515;
}

class A2 extends A
{
  @override
  int f() => 1789;
}

class X<T extends A> extends T
{
  int x() => f();
}

void main()
{
  X<A1> x1 = X<A1>();
  X<A2> x2 = X<A2>();
  
  print( x1.x() ); // Should print 1515;
  print( x2.x() ); // Should print 1789;
}

The critical line is the one defining template class X: the compiler complains because X is inheriting of the template argument (which extends A, so it could know that it is a class).

I may admit that I’m pushing the language a bit far (my C++ background…) but is there a way to perform this in Dart?

Addendum

I tried this:

abstract class A
{
  A();
  
  int f();
}

class A1 extends A
{
  A1();

  @override
  int f() => 1515;
}

class A2 extends A
{
  A2();

  @override
  int f()=>1789;
}

class X<T extends A> {
  late T t;
  
  X(){
    t = T();
  }
  int x() => t.f();
}

void main()
{
  X<A1> x1 = X<A1>();
  X<A2> x2 = X<A2>();
  
  print( x1.x() );
  print( x2.x() );
}

But the compiler is now complaining on the line with t = T();

‘T’ isn’t a function. Try correcting the name to match an existing
function, or to define a method or function name ‘T’.

Solution

Dart has generics, which are similar to but are not the same as C++ templates. C++ templates are more similar to macros where template arguments are substituted at compilation time, and it results in separate compiled versions of the template class for each combination of template arguments. Dart generics are different and instead are like Java’s: there is a single compiled version of the generic class.

You therefore can’t use duck-typing with T like you would with C++, and you can’t invoke T constructors or static methods.

What you instead can do is to provide a callback that constructs a T, e.g.:

class X<T extends A> {
  T t;
  
  X(T Function() makeT) : t = makeT();

  int x() => t.f();
}

void main()
{
  X<A1> x1 = X(A1.new);
  X<A2> x2 = X(A2.new);

  ...
}

(I also made X.t no longer late (late is unnecessary if you initialize it via the constructor’s initializer list) and removed the explicit type parameters when invoking the X constructors (they now can be inferred from the argument).)

Answered By – jamesdlin

Answer Checked By – Timothy Miller (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *