Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

functioning of covariant in flutter

I was going through dart documentation and there I came across this code and this term covariant. I went through some documentation but I didn't get what is its purpose there. A detailed explained answer is always appreciated.

class Animal {
  void chase(Animal x) { ... }
}

class Mouse extends Animal { ... }

class Cat extends Animal {
  @override
  void chase(covariant Mouse x) { ... }
}
like image 347
Manishyadav Avatar asked Sep 01 '25 01:09

Manishyadav


2 Answers

In Dart, if you override a superclass method, the arguments of the override method must have the same type as the original.

Since Animal.chase in your example accepts an argument of Animal, you must do the same in your override:

class Cat extends Animal {
  @override
  void chase(Animal x) { ... }
}

Why? Imagine if there was no such restriction. Cat could define void chase(Mouse x) while Dog could define void chase(Cat x). Then imagine you have a List<Animal> animals and you call chase(cat) on one of them. If the animal is a dog, it'll work, but if the animal is cat, Cat is not a Mouse! The Cat class has no way to handle being asked to chase another Cat.

So you're forced to use void chase(Animal x). We can simulate a void chase(Mouse x) type signature by adding a runtime type check:

void chase(Animal x) {
  if (x is Mouse) {
    /* do chase */
  } else {
    /* throw error */
  }
}

It turns out this is a fairly common operation, and it would be nicer if it could be checked at compile time where possible. So Dart added a covariant operator. Changing the function signature to chase(covariant Mouse x) (where Mouse is a subclass of Animal) does three things:

  1. Allows you to omit the x is Mouse check, as it is done for you.
  2. Creates a compile time error if any Dart code calls Cat.chase(x) where x is not a Mouse or its subclass — if known at compile time.
  3. Creates a runtime error in other cases.

Another example is the operator ==(Object x) method on objects. Say you have a class Point:

You could implement operator== this way:

class Point {
  final int x, y;
  Point(this.x, this.y);

  bool operator==(Object other) {
    if (other is Point) {
      return x == other.x && y == other.y;
    } else {
      return false;
    }
  }
}

But this code compiles even if you compare Point(1,2) == "string" or a number or some other object. It makes no sense to compare a Point with things that aren't Points.

You can use covariant to tell Dart that other should be a Point, otherwise it's an error. This lets you drop the other is Point part, too:

bool operator==(covariant Point other) =>
  x == other.x && y == other.y;

Why is it called 'covariant'?

Covariant is a fancy type theory term, but it basically means 'this class or its subclasses'. Put another way, it means types that are equal or lower in the type hierarchy.

You are explicitly telling Dart to tighten the type checking of this argument to a subclass of the original. For the first example: tightening Animal to Mouse; for the second: tightening Object to Point.

Useful related terms are contravariant, which means types equal or higher in the type hierarchy, and invariant, which means exactly this type.

For more information, this Stack Overflow question is a good resource.

like image 88
rjh Avatar answered Sep 03 '25 00:09

rjh


Just try to remove the key word covariant and it will become self explanatory.

You will receive a compiler error that you are overiding a method with mismatch parameter type Expected: Animal, Actual: Mouse

However, Mouse is a subtype of Animal, so if you want to allow this case without error, add the covariant keyword

Before enter image description here

After

enter image description here

Here you can see the Mouse is subtype of animal

like image 30
TSR Avatar answered Sep 02 '25 23:09

TSR