← Dart EnglishChapter 12 of 13

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 save(T item); } class UserRepository implements Repository { @override Future getById(String id) async => User(id); @override Future> getAll() async => []; @override Future save(User item) async {} } class User { final String id; User(this.id); } ``` ## Generic Aliases ### Typedef ```dart typedef StringMapper = String Function(String); typedef KeyValuePair = MapEntry; typedef NumberList = List; void main() { StringMapper mapper = (s) => s.toUpperCase(); print(mapper('hello')); // HELLO NumberList numbers = [1, 2, 3.0]; print(numbers); // [1, 2, 3.0] } ``` ## Type Constraints ### where Clause ```dart // Show items that are comparable and serializable class Collection> where T extends Serializable { void sortAndPrint() { // T is both Comparable and Serializable } } abstract class Serializable { String toJson(); } ``` ## Variance ### Covariance (out) ```dart // T can only be produced, not consumed class Producer { T produce() => throw UnimplementedError(); } // Covariant type parameters allow: // Producer is a subtype of Producer ``` ### Contravariance (in) ```dart // T can only be consumed, not produced class Consumer { void consume(T item) {} } // Contravariant type parameters allow: // Consumer is a subtype of Consumer ``` ## Generic Restrictions ### Common Restrictions ```dart // T must be non-nullable class NonNullBox { T _value; NonNullBox(this._value); } // T must have default constructor class GenericFactory { T create() => throw UnimplementedError() as T; } ``` ## Summary - Generics: `` for type parameters - Generic classes: `class Box {...}` - Generic functions: `T func(...) {...}` - Bounded: `T extends SomeType` - Collections: `List`, `Map`, `Set` - `typedef` creates type aliases - Variance: `out` (covariant), `in` (contravariant) - Generics provide type safety and reduce code duplication

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →