Generics
## Learning Objectives
- Understand generic types
- Create generic classes and functions
- Master bounded type parameters
- Learn type restrictions
## Why Generics?
### Without Generics
```dart
class Stack {
List _items = [];
void push(dynamic item) => _items.add(item);
dynamic pop() => _items.removeLast();
}
void main() {
var stack = Stack();
stack.push(1);
stack.push('string'); // Allowed but not safe
var item = stack.pop();
// print(item.length); // Runtime error if item is int!
}
```
### With Generics
```dart
class Stack {
List _items = [];
void push(T item) => _items.add(item);
T pop() => _items.removeLast();
}
void main() {
var intStack = Stack();
intStack.push(1);
// intStack.push('string'); // Error!
var stringStack = Stack();
stringStack.push('hello');
print(stringStack.pop().length); // 5
}
```
## Generic Classes
### Box Example
```dart
class Box {
T _value;
Box(this._value);
T get value => _value;
void set value(T value) => _value = value;
}
void main() {
var intBox = Box(42);
print(intBox.value); // 42
var stringBox = Box('Hello');
print(stringBox.value); // Hello
var dynamicBox = Box(42); // Works but loses type safety
// dynamicBox.value is still int, but compiler doesn't know
}
```
### Multiple Type Parameters
```dart
class Pair {
K first;
V second;
Pair(this.first, this.second);
@override
String toString() => '($first, $second)';
}
void main() {
var pair = Pair('age', 30);
print(pair); // (age, 30)
var entry = Pair.of(1, 'one');
print(entry); // (1, one)
}
```
## Generic Functions
### Basic Generic Function
```dart
T first(List list) {
if (list.isEmpty) {
throw Exception('Empty list');
}
return list.first;
}
void main() {
print(first([1, 2, 3])); // 1
print(first(['a', 'b', 'c'])); // a
}
```
### Generic Method
```dart
class Utils {
static T maximum>(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
}
void main() {
print(Utils.maximum(5, 10)); // 10
print(Utils.maximum('apple', 'banana')); // banana
}
```
## Bounded Type Parameters
### extends Constraint
```dart
// T must extend num (int, double, etc.)
class NumericBox {
T _value;
NumericBox(this._value);
T square() => _value * _value as T;
}
void main() {
var intBox = NumericBox(5);
print(intBox.square()); // 25
var doubleBox = NumericBox(3.5);
print(doubleBox.square()); // 12.25
// NumericBox(); // Error: String doesn't extend num
}
```
### Multiple Bounds
```dart
class ComparablePair, V extends Comparable> {
K key;
V value;
ComparablePair(this.key, this.value);
bool isKeyGreater() => key.compareTo(key) > 0;
}
```
## Generic Collections
### List
```dart
void main() {
List numbers = [1, 2, 3, 4, 5];
List names = ['Alice', 'Bob', 'Charlie'];
// List can hold anything
List mixed = [1, 'two', 3.0];
}
```
### Map
```dart
void main() {
Map ages = {
'Alice': 30,
'Bob': 25,
};
Map ids = {
1: 'one',
2: 'two',
};
}
```
### Set
```dart
void main() {
Set numbers = {1, 2, 3, 4, 5};
Set uniqueNames = {'Alice', 'Bob', 'Alice'}; // {'Alice', 'Bob'}
}
```
## Generic Interfaces
### Generic Interface
```dart
abstract class Repository {
Future getById(String id);
Future
- > getAll();
Future
- > getAll() async => [];
@override
Future
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →