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 togetherTry 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