dartgenericscovariancecontravariancegeneric-variance

How to work around the lack of invariant/contravariant type parameters in Dart?


Dart unfortunately lacks (by design) the ability to specify invariant or contravariant type parameters. So when I need them, how do I work around their absence?

Take the abstract setting of a Producer<T> and a Consumer<T> class.

abstract class Producer<T> {
  T produce();
}

abstract class Consumer<T> {
  void consume(T item);
}

The type parameter in Producer should be covariant, but in Consumer it should be contravariant (a concrete example: method parameters are contravariant, return types are covariant). However, Dart will automatically make the type parameter in Consumer covariant, without a way of specifying otherwise.

So if some other code component needs, for example, a Consumer<num>, conceptually it should accept a Consumer<Object>, because if a consumer accepts any object, it will certainly accept an number. But if I just write:

void someFunction(Consumer<num> consumer);

This will accept only Consumer<num> or Consumer<int>, Consumer<float> etc. which will cause errors.

So how should I write it instead? Some options that occurred to me:

void someFunction(Consumer<dynamic> consumer);

This removes static type checking, which I don't want.


Solution

  • There is no workaround, generics are currently covariant. The best you can do is either:

    If the only thing the Consumer class does is to consume, it's a single-method interface, and you can just use a function type, and pass around function values, instead.