Generic Classes
Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 65 of 110.
You've seen how Dart's built-in collections like List<String> use type parameters. Now it's time to create your own generic classes that work with any type you specify.
A generic class uses a type parameter (typically T) as a placeholder for the actual type that will be provided when creating an instance:
class Box<T> {
T content;
Box(this.content);
T getContent() => content;
}
void main() {
var stringBox = Box<String>('Hello');
var intBox = Box<int>(42);
print(stringBox.getContent()); // Hello
print(intBox.getContent()); // 42
}The T acts as a type variable - when you create Box<String>, every T in the class becomes String. This gives you type safety without writing separate classes for each type.
You can use multiple type parameters when your class needs to work with more than one type:
class Pair<K, V> {
K first;
V second;
Pair(this.first, this.second);
}
void main() {
var pair = Pair<String, int>('age', 25);
print('${pair.first}: ${pair.second}'); // age: 25
}Generic classes are the foundation of reusable, type-safe code. Instead of duplicating logic for different types or losing type information with dynamic, generics let you write flexible code that the compiler can still verify.
Challenge
EasyLet's build a gift wrapping system that uses generic classes to create flexible containers for different types of items. You'll create a generic Wrapper class that can wrap any type of content, and a GiftBox class with two type parameters to pair a gift with a card message.
You'll organize your code into two files:
wrapper.dart: Define your generic classes here:- A generic class
Wrapper<T>with aT contentproperty and a constructor. Include a methodunwrap()that returns the content, and a methoddescribe()that printsWrapped: [content] - A generic class
GiftBox<G, C>with two properties:G giftandC card. Include a constructor and a methodopen()that printsGift: [gift]on one line andCard: [card]on the next line
- A generic class
main.dart: Import your wrapper file and demonstrate the generic classes with different types:- Create a
Wrapper<String>containing'Chocolate'and calldescribe() - Create a
Wrapper<int>containing42and calldescribe() - Create a
Wrapper<double>containing3.14and calldescribe() - Print an empty line
- Create a
GiftBox<String, String>with gift'Teddy Bear'and card'Happy Birthday!', then callopen() - Print an empty line
- Create a
GiftBox<int, String>with gift100(representing a gift card amount) and card'Congratulations!', then callopen()
- Create a
Notice how the same Wrapper class works seamlessly with strings, integers, and doubles. The GiftBox class demonstrates using two type parameters to pair different types together - the gift can be any type while the card message remains a string.
Expected output:
Wrapped: Chocolate
Wrapped: 42
Wrapped: 3.14
Gift: Teddy Bear
Card: Happy Birthday!
Gift: 100
Card: Congratulations!Cheat sheet
Generic classes use type parameters (like T) as placeholders for actual types specified when creating instances:
class Box<T> {
T content;
Box(this.content);
T getContent() => content;
}
void main() {
var stringBox = Box<String>('Hello');
var intBox = Box<int>(42);
print(stringBox.getContent()); // Hello
print(intBox.getContent()); // 42
}When you create Box<String>, every T in the class becomes String, providing type safety without separate classes for each type.
Use multiple type parameters when working with more than one type:
class Pair<K, V> {
K first;
V second;
Pair(this.first, this.second);
}
void main() {
var pair = Pair<String, int>('age', 25);
print('${pair.first}: ${pair.second}'); // age: 25
}Try it yourself
import 'wrapper.dart';
void main() {
// TODO: Create a Wrapper<String> containing 'Chocolate' and call describe()
// TODO: Create a Wrapper<int> containing 42 and call describe()
// TODO: Create a Wrapper<double> containing 3.14 and call describe()
// TODO: Print an empty line
// TODO: Create a GiftBox<String, String> with gift 'Teddy Bear'
// and card 'Happy Birthday!', then call open()
// TODO: Print an empty line
// TODO: Create a GiftBox<int, String> with gift 100
// and card 'Congratulations!', then call open()
}
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