Generics
Generics allow you to write type-safe, reusable code that works with different types.
Why Generics?
dart
// Without generics - not type-safe
class Box {
Object? content;
void put(Object item) {
content = item;
}
Object? get() {
return content;
}
}
// With generics - type-safe
class GenericBox<T> {
T? content;
void put(T item) {
content = item;
}
T? get() {
return content;
}
}
void main() {
var intBox = GenericBox<int>();
intBox.put(42);
// intBox.put('text'); // Error: type mismatch
var stringBox = GenericBox<String>();
stringBox.put('Hello');
print(stringBox.get()); // Hello
}Generic Collections
dart
// List
List<int> numbers = [1, 2, 3];
List<String> names = ['Alice', 'Bob'];
// Map
Map<String, int> ages = {'Alice': 25, 'Bob': 30};
// Set
Set<String> colors = {'red', 'green', 'blue'};Generic Functions
dart
T getFirst<T>(List<T> list) {
return list[0];
}
void main() {
print(getFirst<int>([1, 2, 3])); // 1
print(getFirst<String>(['a', 'b'])); // a
print(getFirst([1.5, 2.5])); // 1.5 (type inferred)
}Generic Classes
dart
class Pair<K, V> {
K key;
V value;
Pair(this.key, this.value);
@override
String toString() => 'Pair($key, $value)';
}
void main() {
var pair1 = Pair<String, int>('age', 25);
var pair2 = Pair<int, String>(1, 'first');
print(pair1); // Pair(age, 25)
print(pair2); // Pair(1, first)
}Type Constraints
dart
class NumberBox<T extends num> {
T value;
NumberBox(this.value);
T add(T other) {
return (value + other) as T;
}
}
void main() {
var intBox = NumberBox<int>(10);
print(intBox.add(5)); // 15
var doubleBox = NumberBox<double>(10.5);
print(doubleBox.add(2.5)); // 13.0
// var stringBox = NumberBox<String>('text'); // Error!
}Generic Methods
dart
class Utils {
static T? findFirst<T>(List<T> list, bool Function(T) test) {
for (var item in list) {
if (test(item)) {
return item;
}
}
return null;
}
}
void main() {
var numbers = [1, 2, 3, 4, 5];
var firstEven = Utils.findFirst<int>(numbers, (n) => n % 2 == 0);
print(firstEven); // 2
var names = ['Alice', 'Bob', 'Charlie'];
var longName = Utils.findFirst<String>(names, (n) => n.length > 5);
print(longName); // Charlie
}Complete Example
dart
class Stack<T> {
final List<T> _items = [];
void push(T item) {
_items.add(item);
}
T? pop() {
if (_items.isEmpty) return null;
return _items.removeLast();
}
T? peek() {
if (_items.isEmpty) return null;
return _items.last;
}
bool get isEmpty => _items.isEmpty;
int get size => _items.length;
@override
String toString() => _items.toString();
}
void main() {
var intStack = Stack<int>();
intStack.push(1);
intStack.push(2);
intStack.push(3);
print('Stack: $intStack'); // [1, 2, 3]
print('Pop: ${intStack.pop()}'); // 3
print('Peek: ${intStack.peek()}'); // 2
print('Size: ${intStack.size}'); // 2
}