Dart Generics
Write flexible and reusable code with type safety
🔧 What are Dart Generics?
Generics allow you to write flexible, reusable code that works with different types while maintaining type safety. They help create classes and functions that can work with any data type.
// Generic function that works with any type
T getFirst<T>(List<T> items) {
return items.first;
}
void main() {
print(getFirst([1, 2, 3])); // Works with integers
print(getFirst(['a', 'b', 'c'])); // Works with strings
}
Output:
1
a
Key Generic Concepts
Generic Classes
Classes that work with any type
class Box<T> {
T value;
Box(this.value);
}
Generic Functions
Functions that accept any type
T swap<T>(T a, T b) {
return b;
}
Type Constraints
Limit generics to specific types
class NumberBox<T extends num> {
T value;
NumberBox(this.value);
}
Collections
Built-in generic collections
List<String> names = ['Alice', 'Bob'];
Map<String, int> ages = {'Alice': 25};
🔹 Generic Classes
Create classes that can work with different types:
class Container<T> {
T _item;
Container(this._item);
T get item => _item;
set item(T value) => _item = value;
void display() {
print('Container holds: $_item');
}
}
void main() {
// String container
var stringContainer = Container<String>('Hello');
stringContainer.display();
// Integer container
var intContainer = Container<int>(42);
intContainer.display();
}
Output:
Container holds: Hello
Container holds: 42
🔹 Generic Functions
Functions that work with multiple types:
// Generic function to find maximum
T findMax<T extends Comparable<T>>(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
// Generic function to create pairs
class Pair<T, U> {
T first;
U second;
Pair(this.first, this.second);
@override
String toString() => '($first, $second)';
}
void main() {
print(findMax(10, 5)); // Works with integers
print(findMax('apple', 'zebra')); // Works with strings
var pair = Pair<String, int>('Age', 25);
print(pair);
}
Output:
10
zebra
(Age, 25)
🔹 Type Constraints
Restrict generics to specific types using extends:
// Only accept numeric types
class Calculator<T extends num> {
T add(T a, T b) {
return (a + b) as T;
}
T multiply(T a, T b) {
return (a * b) as T;
}
}
// Only accept types that implement Comparable
class Sorter<T extends Comparable<T>> {
List<T> sort(List<T> items) {
var sorted = List<T>.from(items);
sorted.sort();
return sorted;
}
}
void main() {
var calc = Calculator<double>();
print(calc.add(3.5, 2.1));
var sorter = Sorter<String>();
print(sorter.sort(['banana', 'apple', 'cherry']));
}
Output:
5.6
[apple, banana, cherry]
🔹 Generic Collections
Dart's built-in collections are generic:
void main() {
// Generic List
List<int> numbers = [1, 2, 3, 4, 5];
List<String> fruits = ['apple', 'banana', 'orange'];
// Generic Map
Map<String, int> scores = {
'Alice': 95,
'Bob': 87,
'Charlie': 92
};
// Generic Set
Set<String> uniqueColors = {'red', 'green', 'blue'};
print('Numbers: $numbers');
print('Fruits: $fruits');
print('Scores: $scores');
print('Colors: $uniqueColors');
// Type-safe operations
numbers.add(6); // OK - adding int
// numbers.add('text'); // Error - can't add String to List<int>
}
Output:
Numbers: [1, 2, 3, 4, 5]
Fruits: [apple, banana, orange]
Scores: {Alice: 95, Bob: 87, Charlie: 92}
Colors: {red, green, blue}