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) { ... }
}
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:
x is Mouse
check, as it is done for you.Cat.chase(x)
where x is not a Mouse or its subclass — if known at compile time.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.
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
After
Here you can see the Mouse is subtype of animal
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With