Covariant Keyword
Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 61 of 110.
When you override a method in a subclass, Dart normally requires the parameter types to match exactly. But sometimes you want a subclass method to accept a more specific type than the parent. The covariant keyword allows this.
Consider a scenario where an Animal class has a method that accepts another Animal:
class Animal {
void chase(Animal other) {
print('Chasing an animal');
}
}
class Dog extends Animal {
@override
void chase(covariant Dog other) {
print('Dog chasing another dog');
}
}Without covariant, Dart would complain that Dog isn't the same type as Animal. By adding covariant before the parameter, you tell Dart: "I know this is more specific, and I'll ensure only the right type is passed."
This is useful when subclasses logically should only work with their own kind:
void main() {
Dog dog1 = Dog();
Dog dog2 = Dog();
dog1.chase(dog2); // Works: Dog chasing another dog
}Be careful though - using covariant shifts type safety to runtime. If you call the method through a parent reference with the wrong type, you'll get a runtime error instead of a compile-time error. Use it when you're confident about the types being passed.
Challenge
EasyLet's build a pet compatibility system that uses the covariant keyword to ensure pets can only play with their own kind. You'll create a hierarchy of pets where each type has a playWith method that accepts only the same type of pet.
You'll organize your code into two files:
pets.dart: Define your pet hierarchy here:- A
Petclass with aString nameproperty and a constructor. Include a methodplayWith(Pet other)that prints[name] plays with [other.name] - A
Catclass that extendsPetand overridesplayWithusing thecovariantkeyword to accept only aCat. It should print[name] and [other.name] chase yarn together - A
Dogclass that extendsPetand overridesplayWithusing thecovariantkeyword to accept only aDog. It should print[name] and [other.name] fetch the ball together - A
Rabbitclass that extendsPetand overridesplayWithusing thecovariantkeyword to accept only aRabbit. It should print[name] and [other.name] hop around together
- A
main.dart: Import your pets file and demonstrate how each pet type plays exclusively with its own kind:- Create two
Catobjects named'Whiskers'and'Mittens', then have Whiskers play with Mittens - Create two
Dogobjects named'Buddy'and'Max', then have Buddy play with Max - Create two
Rabbitobjects named'Flopsy'and'Cotton', then have Flopsy play with Cotton
- Create two
The covariant keyword allows each subclass to narrow the parameter type from Pet to its specific type, ensuring type-safe interactions between pets of the same kind.
Expected output:
Whiskers and Mittens chase yarn together
Buddy and Max fetch the ball together
Flopsy and Cotton hop around togetherCheat sheet
The covariant keyword allows a subclass to override a method with a more specific parameter type than the parent class.
Without covariant, Dart requires parameter types to match exactly when overriding methods. With covariant, you can narrow the parameter type in the subclass:
class Animal {
void chase(Animal other) {
print('Chasing an animal');
}
}
class Dog extends Animal {
@override
void chase(covariant Dog other) {
print('Dog chasing another dog');
}
}This is useful when subclasses should only work with their own type:
void main() {
Dog dog1 = Dog();
Dog dog2 = Dog();
dog1.chase(dog2); // Works: Dog chasing another dog
}Important: Using covariant shifts type safety from compile-time to runtime. If you call the method through a parent reference with the wrong type, you'll get a runtime error instead of a compile-time error.
Try it yourself
import 'pets.dart';
void main() {
// TODO: Create two Cat objects named 'Whiskers' and 'Mittens'
// Then have Whiskers play with Mittens
// TODO: Create two Dog objects named 'Buddy' and 'Max'
// Then have Buddy play with Max
// TODO: Create two Rabbit objects named 'Flopsy' and 'Cotton'
// Then have Flopsy play with Cotton
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesLibraries & ImportsIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsInstance VariablesConstructor BasicsRecap - Simple Calculator4Null Safety
Intro to Null SafetyNullable vs Non-NullableThe ? and ! OperatorsLate Keyword & Null SafetyNull-Aware OperatorsNull Safety in ClassesRecap - User Profile System7Abstract Classes & Interfaces
Abstract ClassesAbstract MethodsInterfaces in DartImplicit InterfacesImplementing vs ExtendingMultiple InterfacesRecap - Shape Calculator10Collections & Generics
List, Set, Map OverviewType-Safe CollectionsGeneric ClassesGeneric MethodsGeneric ConstraintsIterable & IteratorRecap - Generic Storage13Advanced OOP Concepts
Composition vs InheritanceExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums with Methods16Project: Library Management
Project OverviewBook and User Classes2Constructors in Dart
Default ConstructorNamed ConstructorsInitializer ListsConstant ConstructorsFactory ConstructorsRedirecting ConstructorsRecap - Shape Builder5Encapsulation
Public vs Private MembersThe _ Prefix ConventionLibrary-Level PrivacyGetters & Setters DepthInformation HidingRecap - Student Records8Mixins
Introduction to MixinsCreating MixinsUsing Multiple Mixinson Keyword in MixinsMixin vs InheritanceMixin vs InterfaceRecap - Animal System11Special Methods
toString() OverridehashCode & == OverrideComparable Interfacecall() MethodnoSuchMethod OverrideRecap - Custom Collection14Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern3Class Properties
Instance vs Static MembersFinal & Const FieldsLate VariablesStatic Methods & FieldsGetters and SettersRecap - Bank Account Manager6Inheritance
Basic InheritanceThe super KeywordMethod OverridingThe @override AnnotationThe final Class KeywordConstructors & InheritanceRecap - Employee Hierarchy9Polymorphism
Polymorphism BasicsPolymorphism via InterfacesRuntime Type CheckingThe is & as OperatorsCovariant KeywordRecap - Payment Processor