[{"data":1,"prerenderedAt":3687},["ShallowReactive",2],{"page-\u002Fdart\u002F12-generics":3},{"id":4,"title":5,"body":6,"description":3680,"extension":3681,"meta":3682,"navigation":51,"path":3683,"seo":3684,"stem":3685,"__hash__":3686},"content\u002Fdart\u002F12-generics.md","Dart — Reified Generics, Bounds & Covariance",{"type":7,"value":8,"toc":3666},"minimark",[9,13,18,415,419,881,885,1634,1638,3662],[10,11,5],"h1",{"id":12},"dart-reified-generics-bounds-covariance",[14,15,17],"h2",{"id":16},"reified-generics-runtime-type-information","Reified Generics — Runtime Type Information",[19,20,22],"code-wrapper",{"language":21},"dart",[23,24,28],"pre",{"className":25,"code":26,"language":21,"meta":27,"style":27},"language-dart shiki shiki-themes github-light github-dark","\u002F\u002F Dart generics are REIFIED — type parameters exist at runtime.\n\u002F\u002F Unlike Java's type erasure, you can check `is List\u003Cint>` at runtime.\n\nvar list = \u003Cint>[1, 2, 3];\nprint(list.runtimeType);  \u002F\u002F List\u003Cint> — the type is preserved at runtime\nprint(list is List\u003Cint>);  \u002F\u002F true\nprint(list is List\u003Cnum>);  \u002F\u002F true (covariance: List\u003Cint> is a List\u003Cnum>)\nprint(list is List\u003CString>);  \u002F\u002F false\n\n\u002F\u002F Contrast with Java: `list instanceof List\u003CInteger>` doesn't compile (erased).\n\u002F\u002F Dart's reified generics enable runtime type dispatch.\n\n\u002F\u002F Using reified types in practice:\nvoid processList(List\u003Cdynamic> items) {\n  if (items is List\u003Cint>) {\n    \u002F\u002F items is promoted to List\u003Cint> here — reified check enables this.\n    print('Int list, sum: ${items.fold(0, (a, b) => a + b)}');\n  } else if (items is List\u003CString>) {\n    print('String list: ${items.join(\", \")}');\n  } else {\n    print('Unknown list type: ${items.runtimeType}');\n  }\n}\n","",[29,30,31,40,46,53,97,110,135,156,177,182,188,194,199,205,228,248,254,313,337,367,377,403,409],"code",{"__ignoreMap":27},[32,33,36],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"sdCPZ","\u002F\u002F Dart generics are REIFIED — type parameters exist at runtime.\n",[32,41,43],{"class":34,"line":42},2,[32,44,45],{"class":38},"\u002F\u002F Unlike Java's type erasure, you can check `is List\u003Cint>` at runtime.\n",[32,47,49],{"class":34,"line":48},3,[32,50,52],{"emptyLinePlaceholder":51},true,"\n",[32,54,56,60,64,67,70,74,77,80,83,86,89,91,94],{"class":34,"line":55},4,[32,57,59],{"class":58},"svdQ7","var",[32,61,63],{"class":62},"ssxIu"," list ",[32,65,66],{"class":58},"=",[32,68,69],{"class":58}," \u003C",[32,71,73],{"class":72},"snvgF","int",[32,75,76],{"class":58},">",[32,78,79],{"class":62},"[",[32,81,82],{"class":72},"1",[32,84,85],{"class":62},", ",[32,87,88],{"class":72},"2",[32,90,85],{"class":62},[32,92,93],{"class":72},"3",[32,95,96],{"class":62},"];\n",[32,98,100,104,107],{"class":34,"line":99},5,[32,101,103],{"class":102},"sIsaT","print",[32,105,106],{"class":62},"(list.runtimeType);  ",[32,108,109],{"class":38},"\u002F\u002F List\u003Cint> — the type is preserved at runtime\n",[32,111,113,115,118,121,124,127,129,132],{"class":34,"line":112},6,[32,114,103],{"class":102},[32,116,117],{"class":62},"(list ",[32,119,120],{"class":58},"is",[32,122,123],{"class":72}," List",[32,125,126],{"class":62},"\u003C",[32,128,73],{"class":72},[32,130,131],{"class":62},">);  ",[32,133,134],{"class":38},"\u002F\u002F true\n",[32,136,138,140,142,144,146,148,151,153],{"class":34,"line":137},7,[32,139,103],{"class":102},[32,141,117],{"class":62},[32,143,120],{"class":58},[32,145,123],{"class":72},[32,147,126],{"class":62},[32,149,150],{"class":72},"num",[32,152,131],{"class":62},[32,154,155],{"class":38},"\u002F\u002F true (covariance: List\u003Cint> is a List\u003Cnum>)\n",[32,157,159,161,163,165,167,169,172,174],{"class":34,"line":158},8,[32,160,103],{"class":102},[32,162,117],{"class":62},[32,164,120],{"class":58},[32,166,123],{"class":72},[32,168,126],{"class":62},[32,170,171],{"class":72},"String",[32,173,131],{"class":62},[32,175,176],{"class":38},"\u002F\u002F false\n",[32,178,180],{"class":34,"line":179},9,[32,181,52],{"emptyLinePlaceholder":51},[32,183,185],{"class":34,"line":184},10,[32,186,187],{"class":38},"\u002F\u002F Contrast with Java: `list instanceof List\u003CInteger>` doesn't compile (erased).\n",[32,189,191],{"class":34,"line":190},11,[32,192,193],{"class":38},"\u002F\u002F Dart's reified generics enable runtime type dispatch.\n",[32,195,197],{"class":34,"line":196},12,[32,198,52],{"emptyLinePlaceholder":51},[32,200,202],{"class":34,"line":201},13,[32,203,204],{"class":38},"\u002F\u002F Using reified types in practice:\n",[32,206,208,211,214,217,220,222,225],{"class":34,"line":207},14,[32,209,210],{"class":58},"void",[32,212,213],{"class":102}," processList",[32,215,216],{"class":62},"(",[32,218,219],{"class":72},"List",[32,221,126],{"class":62},[32,223,224],{"class":72},"dynamic",[32,226,227],{"class":62},"> items) {\n",[32,229,231,234,237,239,241,243,245],{"class":34,"line":230},15,[32,232,233],{"class":58},"  if",[32,235,236],{"class":62}," (items ",[32,238,120],{"class":58},[32,240,123],{"class":72},[32,242,126],{"class":62},[32,244,73],{"class":72},[32,246,247],{"class":62},">) {\n",[32,249,251],{"class":34,"line":250},16,[32,252,253],{"class":38},"    \u002F\u002F items is promoted to List\u003Cint> here — reified check enables this.\n",[32,255,257,260,262,266,269,272,275,278,280,283,286,289,291,294,297,299,302,304,307,310],{"class":34,"line":256},17,[32,258,259],{"class":102},"    print",[32,261,216],{"class":62},[32,263,265],{"class":264},"sJ6F3","'Int list, sum: ",[32,267,268],{"class":264},"${",[32,270,271],{"class":72},"items",[32,273,274],{"class":264},".",[32,276,277],{"class":102},"fold",[32,279,216],{"class":264},[32,281,282],{"class":72},"0",[32,284,285],{"class":264},", (",[32,287,288],{"class":72},"a",[32,290,85],{"class":264},[32,292,293],{"class":72},"b",[32,295,296],{"class":264},") => ",[32,298,288],{"class":72},[32,300,301],{"class":264}," + ",[32,303,293],{"class":72},[32,305,306],{"class":264},")}",[32,308,309],{"class":264},"'",[32,311,312],{"class":62},");\n",[32,314,316,319,322,325,327,329,331,333,335],{"class":34,"line":315},18,[32,317,318],{"class":62},"  } ",[32,320,321],{"class":58},"else",[32,323,324],{"class":58}," if",[32,326,236],{"class":62},[32,328,120],{"class":58},[32,330,123],{"class":72},[32,332,126],{"class":62},[32,334,171],{"class":72},[32,336,247],{"class":62},[32,338,340,342,344,347,349,351,353,356,358,361,363,365],{"class":34,"line":339},19,[32,341,259],{"class":102},[32,343,216],{"class":62},[32,345,346],{"class":264},"'String list: ",[32,348,268],{"class":264},[32,350,271],{"class":72},[32,352,274],{"class":264},[32,354,355],{"class":102},"join",[32,357,216],{"class":264},[32,359,360],{"class":264},"\", \"",[32,362,306],{"class":264},[32,364,309],{"class":264},[32,366,312],{"class":62},[32,368,370,372,374],{"class":34,"line":369},20,[32,371,318],{"class":62},[32,373,321],{"class":58},[32,375,376],{"class":62}," {\n",[32,378,380,382,384,387,389,391,393,396,399,401],{"class":34,"line":379},21,[32,381,259],{"class":102},[32,383,216],{"class":62},[32,385,386],{"class":264},"'Unknown list type: ",[32,388,268],{"class":264},[32,390,271],{"class":72},[32,392,274],{"class":264},[32,394,395],{"class":72},"runtimeType",[32,397,398],{"class":264},"}",[32,400,309],{"class":264},[32,402,312],{"class":62},[32,404,406],{"class":34,"line":405},22,[32,407,408],{"class":62},"  }\n",[32,410,412],{"class":34,"line":411},23,[32,413,414],{"class":62},"}\n",[14,416,418],{"id":417},"generic-classes-type-safe-collections","Generic Classes — Type-Safe Collections",[19,420,421],{"language":21},[23,422,424],{"className":25,"code":423,"language":21,"meta":27,"style":27},"\u002F\u002F A generic Stack with type-safe push\u002Fpop.\nclass Stack\u003CT> {\n  final _items = \u003CT>[];  \u002F\u002F typed internal storage\n\n  void push(T item) => _items.add(item);  \u002F\u002F only T accepted\n\n  T pop() {\n    if (_items.isEmpty) throw StateError('Stack is empty');\n    return _items.removeLast();  \u002F\u002F returns T (not dynamic)\n  }\n\n  T peek() {\n    if (_items.isEmpty) throw StateError('Stack is empty');\n    return _items.last;\n  }\n\n  bool get isEmpty => _items.isEmpty;\n  int get length => _items.length;\n\n  \u002F\u002F Generic method on a generic class — U is independent of T.\n  List\u003CU> map\u003CU>(U Function(T) fn) => _items.map(fn).toList();\n}\n\n\u002F\u002F T is inferred at construction:\nvar stack = Stack\u003CString>();  \u002F\u002F T = String\nstack.push('hello');\nvar item = stack.pop();  \u002F\u002F item is String (typed, not dynamic)\n\u002F\u002F stack.push(42);  \u002F\u002F ✗ compile error: int is not String\n\nvar intStack = Stack\u003Cint>();  \u002F\u002F T = int\nintStack.push(42);\nvar doubled = intStack.map((n) => n * 2);  \u002F\u002F U = int, returns List\u003Cint>\nvar asStrings = intStack.map((n) => n.toString());  \u002F\u002F U = String, returns List\u003CString>\n",[29,425,426,431,447,469,473,503,507,518,539,555,559,563,572,588,595,599,603,619,634,638,643,693,697,701,707,729,745,766,775,780,801,816,851],{"__ignoreMap":27},[32,427,428],{"class":34,"line":35},[32,429,430],{"class":38},"\u002F\u002F A generic Stack with type-safe push\u002Fpop.\n",[32,432,433,436,439,441,444],{"class":34,"line":42},[32,434,435],{"class":58},"class",[32,437,438],{"class":72}," Stack",[32,440,126],{"class":62},[32,442,443],{"class":72},"T",[32,445,446],{"class":62},"> {\n",[32,448,449,452,455,457,459,461,463,466],{"class":34,"line":48},[32,450,451],{"class":58},"  final",[32,453,454],{"class":62}," _items ",[32,456,66],{"class":58},[32,458,69],{"class":58},[32,460,443],{"class":72},[32,462,76],{"class":58},[32,464,465],{"class":62},"[];  ",[32,467,468],{"class":38},"\u002F\u002F typed internal storage\n",[32,470,471],{"class":34,"line":55},[32,472,52],{"emptyLinePlaceholder":51},[32,474,475,478,481,483,485,488,491,494,497,500],{"class":34,"line":99},[32,476,477],{"class":58},"  void",[32,479,480],{"class":102}," push",[32,482,216],{"class":62},[32,484,443],{"class":72},[32,486,487],{"class":62}," item) ",[32,489,490],{"class":58},"=>",[32,492,493],{"class":62}," _items.",[32,495,496],{"class":102},"add",[32,498,499],{"class":62},"(item);  ",[32,501,502],{"class":38},"\u002F\u002F only T accepted\n",[32,504,505],{"class":34,"line":112},[32,506,52],{"emptyLinePlaceholder":51},[32,508,509,512,515],{"class":34,"line":137},[32,510,511],{"class":72},"  T",[32,513,514],{"class":102}," pop",[32,516,517],{"class":62},"() {\n",[32,519,520,523,526,529,532,534,537],{"class":34,"line":158},[32,521,522],{"class":58},"    if",[32,524,525],{"class":62}," (_items.isEmpty) ",[32,527,528],{"class":58},"throw",[32,530,531],{"class":72}," StateError",[32,533,216],{"class":62},[32,535,536],{"class":264},"'Stack is empty'",[32,538,312],{"class":62},[32,540,541,544,546,549,552],{"class":34,"line":179},[32,542,543],{"class":58},"    return",[32,545,493],{"class":62},[32,547,548],{"class":102},"removeLast",[32,550,551],{"class":62},"();  ",[32,553,554],{"class":38},"\u002F\u002F returns T (not dynamic)\n",[32,556,557],{"class":34,"line":184},[32,558,408],{"class":62},[32,560,561],{"class":34,"line":190},[32,562,52],{"emptyLinePlaceholder":51},[32,564,565,567,570],{"class":34,"line":196},[32,566,511],{"class":72},[32,568,569],{"class":102}," peek",[32,571,517],{"class":62},[32,573,574,576,578,580,582,584,586],{"class":34,"line":201},[32,575,522],{"class":58},[32,577,525],{"class":62},[32,579,528],{"class":58},[32,581,531],{"class":72},[32,583,216],{"class":62},[32,585,536],{"class":264},[32,587,312],{"class":62},[32,589,590,592],{"class":34,"line":207},[32,591,543],{"class":58},[32,593,594],{"class":62}," _items.last;\n",[32,596,597],{"class":34,"line":230},[32,598,408],{"class":62},[32,600,601],{"class":34,"line":250},[32,602,52],{"emptyLinePlaceholder":51},[32,604,605,608,611,614,616],{"class":34,"line":256},[32,606,607],{"class":72},"  bool",[32,609,610],{"class":58}," get",[32,612,613],{"class":62}," isEmpty ",[32,615,490],{"class":58},[32,617,618],{"class":62}," _items.isEmpty;\n",[32,620,621,624,626,629,631],{"class":34,"line":315},[32,622,623],{"class":72},"  int",[32,625,610],{"class":58},[32,627,628],{"class":62}," length ",[32,630,490],{"class":58},[32,632,633],{"class":62}," _items.length;\n",[32,635,636],{"class":34,"line":339},[32,637,52],{"emptyLinePlaceholder":51},[32,639,640],{"class":34,"line":369},[32,641,642],{"class":38},"  \u002F\u002F Generic method on a generic class — U is independent of T.\n",[32,644,645,648,650,653,656,659,661,663,666,668,671,673,675,678,680,682,684,687,690],{"class":34,"line":379},[32,646,647],{"class":72},"  List",[32,649,126],{"class":62},[32,651,652],{"class":72},"U",[32,654,655],{"class":62},"> ",[32,657,658],{"class":102},"map",[32,660,126],{"class":62},[32,662,652],{"class":72},[32,664,665],{"class":62},">(",[32,667,652],{"class":72},[32,669,670],{"class":72}," Function",[32,672,216],{"class":62},[32,674,443],{"class":72},[32,676,677],{"class":62},") fn) ",[32,679,490],{"class":58},[32,681,493],{"class":62},[32,683,658],{"class":102},[32,685,686],{"class":62},"(fn).",[32,688,689],{"class":102},"toList",[32,691,692],{"class":62},"();\n",[32,694,695],{"class":34,"line":405},[32,696,414],{"class":62},[32,698,699],{"class":34,"line":411},[32,700,52],{"emptyLinePlaceholder":51},[32,702,704],{"class":34,"line":703},24,[32,705,706],{"class":38},"\u002F\u002F T is inferred at construction:\n",[32,708,710,712,715,717,719,721,723,726],{"class":34,"line":709},25,[32,711,59],{"class":58},[32,713,714],{"class":62}," stack ",[32,716,66],{"class":58},[32,718,438],{"class":72},[32,720,126],{"class":62},[32,722,171],{"class":72},[32,724,725],{"class":62},">();  ",[32,727,728],{"class":38},"\u002F\u002F T = String\n",[32,730,732,735,738,740,743],{"class":34,"line":731},26,[32,733,734],{"class":62},"stack.",[32,736,737],{"class":102},"push",[32,739,216],{"class":62},[32,741,742],{"class":264},"'hello'",[32,744,312],{"class":62},[32,746,748,750,753,755,758,761,763],{"class":34,"line":747},27,[32,749,59],{"class":58},[32,751,752],{"class":62}," item ",[32,754,66],{"class":58},[32,756,757],{"class":62}," stack.",[32,759,760],{"class":102},"pop",[32,762,551],{"class":62},[32,764,765],{"class":38},"\u002F\u002F item is String (typed, not dynamic)\n",[32,767,769,772],{"class":34,"line":768},28,[32,770,771],{"class":38},"\u002F\u002F stack.push(42);",[32,773,774],{"class":38},"  \u002F\u002F ✗ compile error: int is not String\n",[32,776,778],{"class":34,"line":777},29,[32,779,52],{"emptyLinePlaceholder":51},[32,781,783,785,788,790,792,794,796,798],{"class":34,"line":782},30,[32,784,59],{"class":58},[32,786,787],{"class":62}," intStack ",[32,789,66],{"class":58},[32,791,438],{"class":72},[32,793,126],{"class":62},[32,795,73],{"class":72},[32,797,725],{"class":62},[32,799,800],{"class":38},"\u002F\u002F T = int\n",[32,802,804,807,809,811,814],{"class":34,"line":803},31,[32,805,806],{"class":62},"intStack.",[32,808,737],{"class":102},[32,810,216],{"class":62},[32,812,813],{"class":72},"42",[32,815,312],{"class":62},[32,817,819,821,824,826,829,831,834,836,839,842,845,848],{"class":34,"line":818},32,[32,820,59],{"class":58},[32,822,823],{"class":62}," doubled ",[32,825,66],{"class":58},[32,827,828],{"class":62}," intStack.",[32,830,658],{"class":102},[32,832,833],{"class":62},"((n) ",[32,835,490],{"class":58},[32,837,838],{"class":62}," n ",[32,840,841],{"class":58},"*",[32,843,844],{"class":72}," 2",[32,846,847],{"class":62},");  ",[32,849,850],{"class":38},"\u002F\u002F U = int, returns List\u003Cint>\n",[32,852,854,856,859,861,863,865,867,869,872,875,878],{"class":34,"line":853},33,[32,855,59],{"class":58},[32,857,858],{"class":62}," asStrings ",[32,860,66],{"class":58},[32,862,828],{"class":62},[32,864,658],{"class":102},[32,866,833],{"class":62},[32,868,490],{"class":58},[32,870,871],{"class":62}," n.",[32,873,874],{"class":102},"toString",[32,876,877],{"class":62},"());  ",[32,879,880],{"class":38},"\u002F\u002F U = String, returns List\u003CString>\n",[14,882,884],{"id":883},"bounded-type-parameters-constraining-t","Bounded Type Parameters — Constraining T",[19,886,887],{"language":21},[23,888,890],{"className":25,"code":889,"language":21,"meta":27,"style":27},"\u002F\u002F `T extends Model` — T must be a Model subtype. Inside the class,\n\u002F\u002F you can call Model's methods on T values.\n\nabstract class Model {\n  int get id;\n  Map\u003CString, dynamic> toJson();\n}\n\nclass Repository\u003CT extends Model> {\n  final List\u003CT> _items = [];\n\n  void add(T item) => _items.add(item);\n\n  \u002F\u002F Can call `item.id` because T extends Model.\n  T? findById(int id) {\n    for (var item in _items) {\n      if (item.id == id) return item;  \u002F\u002F ✓ .id is available (from Model bound)\n    }\n    return null;\n  }\n\n  List\u003CMap\u003CString, dynamic>> allJson() =>\n      _items.map((item) => item.toJson()).toList();  \u002F\u002F ✓ .toJson() available\n}\n\nclass User extends Model {\n  @override final int id;\n  final String name;\n  User(this.id, this.name);\n\n  @override\n  Map\u003CString, dynamic> toJson() => {'id': id, 'name': name};\n}\n\nvar repo = Repository\u003CUser>();  \u002F\u002F ✓ User extends Model\n\u002F\u002F var bad = Repository\u003CString>();  \u002F\u002F ✗ String doesn't extend Model\nrepo.add(User(1, 'Alice'));\nprint(repo.findById(1)?.name);  \u002F\u002F 'Alice'\n\n\u002F\u002F ── Self-bounded: T extends Comparable\u003CT> ──\n\u002F\u002F T must be comparable to itself — enables sorting.\nclass SortedList\u003CT extends Comparable\u003CT>> {\n  final _items = \u003CT>[];\n\n  void add(T item) {\n    _items.add(item);\n    _items.sort((a, b) => a.compareTo(b));  \u002F\u002F ✓ compareTo available\n  }\n\n  List\u003CT> get items => List.unmodifiable(_items);\n}\n\n\u002F\u002F `T extends Object` — excludes Null (default bound is Object?).\nclass Cache\u003CT extends Object> {\n  final _cache = \u003CString, T>{};\n  T? get(String key) => _cache[key];  \u002F\u002F T is non-nullable (excludes Null)\n  void set(String key, T value) => _cache[key] = value;\n}\n",[29,891,892,897,902,906,919,928,948,952,956,974,992,996,1018,1022,1027,1044,1062,1085,1090,1100,1104,1108,1137,1164,1168,1172,1185,1198,1208,1226,1230,1235,1275,1279,1284,1306,1315,1339,1365,1370,1376,1382,1406,1424,1429,1443,1453,1478,1483,1488,1517,1522,1527,1533,1552,1575,1599,1629],{"__ignoreMap":27},[32,893,894],{"class":34,"line":35},[32,895,896],{"class":38},"\u002F\u002F `T extends Model` — T must be a Model subtype. Inside the class,\n",[32,898,899],{"class":34,"line":42},[32,900,901],{"class":38},"\u002F\u002F you can call Model's methods on T values.\n",[32,903,904],{"class":34,"line":48},[32,905,52],{"emptyLinePlaceholder":51},[32,907,908,911,914,917],{"class":34,"line":55},[32,909,910],{"class":58},"abstract",[32,912,913],{"class":58}," class",[32,915,916],{"class":72}," Model",[32,918,376],{"class":62},[32,920,921,923,925],{"class":34,"line":99},[32,922,623],{"class":72},[32,924,610],{"class":58},[32,926,927],{"class":62}," id;\n",[32,929,930,933,935,937,939,941,943,946],{"class":34,"line":112},[32,931,932],{"class":72},"  Map",[32,934,126],{"class":62},[32,936,171],{"class":72},[32,938,85],{"class":62},[32,940,224],{"class":72},[32,942,655],{"class":62},[32,944,945],{"class":102},"toJson",[32,947,692],{"class":62},[32,949,950],{"class":34,"line":137},[32,951,414],{"class":62},[32,953,954],{"class":34,"line":158},[32,955,52],{"emptyLinePlaceholder":51},[32,957,958,960,963,965,967,970,972],{"class":34,"line":179},[32,959,435],{"class":58},[32,961,962],{"class":72}," Repository",[32,964,126],{"class":62},[32,966,443],{"class":72},[32,968,969],{"class":58}," extends",[32,971,916],{"class":72},[32,973,446],{"class":62},[32,975,976,978,980,982,984,987,989],{"class":34,"line":184},[32,977,451],{"class":58},[32,979,123],{"class":72},[32,981,126],{"class":62},[32,983,443],{"class":72},[32,985,986],{"class":62},"> _items ",[32,988,66],{"class":58},[32,990,991],{"class":62}," [];\n",[32,993,994],{"class":34,"line":190},[32,995,52],{"emptyLinePlaceholder":51},[32,997,998,1000,1003,1005,1007,1009,1011,1013,1015],{"class":34,"line":196},[32,999,477],{"class":58},[32,1001,1002],{"class":102}," add",[32,1004,216],{"class":62},[32,1006,443],{"class":72},[32,1008,487],{"class":62},[32,1010,490],{"class":58},[32,1012,493],{"class":62},[32,1014,496],{"class":102},[32,1016,1017],{"class":62},"(item);\n",[32,1019,1020],{"class":34,"line":201},[32,1021,52],{"emptyLinePlaceholder":51},[32,1023,1024],{"class":34,"line":207},[32,1025,1026],{"class":38},"  \u002F\u002F Can call `item.id` because T extends Model.\n",[32,1028,1029,1031,1034,1037,1039,1041],{"class":34,"line":230},[32,1030,511],{"class":72},[32,1032,1033],{"class":58},"?",[32,1035,1036],{"class":102}," findById",[32,1038,216],{"class":62},[32,1040,73],{"class":72},[32,1042,1043],{"class":62}," id) {\n",[32,1045,1046,1049,1052,1054,1056,1059],{"class":34,"line":250},[32,1047,1048],{"class":58},"    for",[32,1050,1051],{"class":62}," (",[32,1053,59],{"class":58},[32,1055,752],{"class":62},[32,1057,1058],{"class":58},"in",[32,1060,1061],{"class":62}," _items) {\n",[32,1063,1064,1067,1070,1073,1076,1079,1082],{"class":34,"line":256},[32,1065,1066],{"class":58},"      if",[32,1068,1069],{"class":62}," (item.id ",[32,1071,1072],{"class":58},"==",[32,1074,1075],{"class":62}," id) ",[32,1077,1078],{"class":58},"return",[32,1080,1081],{"class":62}," item;  ",[32,1083,1084],{"class":38},"\u002F\u002F ✓ .id is available (from Model bound)\n",[32,1086,1087],{"class":34,"line":315},[32,1088,1089],{"class":62},"    }\n",[32,1091,1092,1094,1097],{"class":34,"line":339},[32,1093,543],{"class":58},[32,1095,1096],{"class":72}," null",[32,1098,1099],{"class":62},";\n",[32,1101,1102],{"class":34,"line":369},[32,1103,408],{"class":62},[32,1105,1106],{"class":34,"line":379},[32,1107,52],{"emptyLinePlaceholder":51},[32,1109,1110,1112,1114,1117,1119,1121,1123,1125,1128,1131,1134],{"class":34,"line":405},[32,1111,647],{"class":72},[32,1113,126],{"class":62},[32,1115,1116],{"class":72},"Map",[32,1118,126],{"class":62},[32,1120,171],{"class":72},[32,1122,85],{"class":62},[32,1124,224],{"class":72},[32,1126,1127],{"class":62},">> ",[32,1129,1130],{"class":102},"allJson",[32,1132,1133],{"class":62},"() ",[32,1135,1136],{"class":58},"=>\n",[32,1138,1139,1142,1144,1147,1149,1152,1154,1157,1159,1161],{"class":34,"line":411},[32,1140,1141],{"class":62},"      _items.",[32,1143,658],{"class":102},[32,1145,1146],{"class":62},"((item) ",[32,1148,490],{"class":58},[32,1150,1151],{"class":62}," item.",[32,1153,945],{"class":102},[32,1155,1156],{"class":62},"()).",[32,1158,689],{"class":102},[32,1160,551],{"class":62},[32,1162,1163],{"class":38},"\u002F\u002F ✓ .toJson() available\n",[32,1165,1166],{"class":34,"line":703},[32,1167,414],{"class":62},[32,1169,1170],{"class":34,"line":709},[32,1171,52],{"emptyLinePlaceholder":51},[32,1173,1174,1176,1179,1181,1183],{"class":34,"line":731},[32,1175,435],{"class":58},[32,1177,1178],{"class":72}," User",[32,1180,969],{"class":58},[32,1182,916],{"class":72},[32,1184,376],{"class":62},[32,1186,1187,1190,1193,1196],{"class":34,"line":747},[32,1188,1189],{"class":58},"  @override",[32,1191,1192],{"class":58}," final",[32,1194,1195],{"class":72}," int",[32,1197,927],{"class":62},[32,1199,1200,1202,1205],{"class":34,"line":768},[32,1201,451],{"class":58},[32,1203,1204],{"class":72}," String",[32,1206,1207],{"class":62}," name;\n",[32,1209,1210,1213,1215,1218,1221,1223],{"class":34,"line":777},[32,1211,1212],{"class":72},"  User",[32,1214,216],{"class":62},[32,1216,1217],{"class":72},"this",[32,1219,1220],{"class":62},".id, ",[32,1222,1217],{"class":72},[32,1224,1225],{"class":62},".name);\n",[32,1227,1228],{"class":34,"line":782},[32,1229,52],{"emptyLinePlaceholder":51},[32,1231,1232],{"class":34,"line":803},[32,1233,1234],{"class":58},"  @override\n",[32,1236,1237,1239,1241,1243,1245,1247,1249,1251,1253,1255,1258,1261,1264,1267,1270,1272],{"class":34,"line":818},[32,1238,932],{"class":72},[32,1240,126],{"class":62},[32,1242,171],{"class":72},[32,1244,85],{"class":62},[32,1246,224],{"class":72},[32,1248,655],{"class":62},[32,1250,945],{"class":102},[32,1252,1133],{"class":62},[32,1254,490],{"class":58},[32,1256,1257],{"class":62}," {",[32,1259,1260],{"class":264},"'id'",[32,1262,1263],{"class":58},":",[32,1265,1266],{"class":62}," id, ",[32,1268,1269],{"class":264},"'name'",[32,1271,1263],{"class":58},[32,1273,1274],{"class":62}," name};\n",[32,1276,1277],{"class":34,"line":853},[32,1278,414],{"class":62},[32,1280,1282],{"class":34,"line":1281},34,[32,1283,52],{"emptyLinePlaceholder":51},[32,1285,1287,1289,1292,1294,1296,1298,1301,1303],{"class":34,"line":1286},35,[32,1288,59],{"class":58},[32,1290,1291],{"class":62}," repo ",[32,1293,66],{"class":58},[32,1295,962],{"class":72},[32,1297,126],{"class":62},[32,1299,1300],{"class":72},"User",[32,1302,725],{"class":62},[32,1304,1305],{"class":38},"\u002F\u002F ✓ User extends Model\n",[32,1307,1309,1312],{"class":34,"line":1308},36,[32,1310,1311],{"class":38},"\u002F\u002F var bad = Repository\u003CString>();",[32,1313,1314],{"class":38},"  \u002F\u002F ✗ String doesn't extend Model\n",[32,1316,1318,1321,1323,1325,1327,1329,1331,1333,1336],{"class":34,"line":1317},37,[32,1319,1320],{"class":62},"repo.",[32,1322,496],{"class":102},[32,1324,216],{"class":62},[32,1326,1300],{"class":72},[32,1328,216],{"class":62},[32,1330,82],{"class":72},[32,1332,85],{"class":62},[32,1334,1335],{"class":264},"'Alice'",[32,1337,1338],{"class":62},"));\n",[32,1340,1342,1344,1347,1350,1352,1354,1357,1359,1362],{"class":34,"line":1341},38,[32,1343,103],{"class":102},[32,1345,1346],{"class":62},"(repo.",[32,1348,1349],{"class":102},"findById",[32,1351,216],{"class":62},[32,1353,82],{"class":72},[32,1355,1356],{"class":62},")",[32,1358,1033],{"class":58},[32,1360,1361],{"class":62},".name);  ",[32,1363,1364],{"class":38},"\u002F\u002F 'Alice'\n",[32,1366,1368],{"class":34,"line":1367},39,[32,1369,52],{"emptyLinePlaceholder":51},[32,1371,1373],{"class":34,"line":1372},40,[32,1374,1375],{"class":38},"\u002F\u002F ── Self-bounded: T extends Comparable\u003CT> ──\n",[32,1377,1379],{"class":34,"line":1378},41,[32,1380,1381],{"class":38},"\u002F\u002F T must be comparable to itself — enables sorting.\n",[32,1383,1385,1387,1390,1392,1394,1396,1399,1401,1403],{"class":34,"line":1384},42,[32,1386,435],{"class":58},[32,1388,1389],{"class":72}," SortedList",[32,1391,126],{"class":62},[32,1393,443],{"class":72},[32,1395,969],{"class":58},[32,1397,1398],{"class":72}," Comparable",[32,1400,126],{"class":62},[32,1402,443],{"class":72},[32,1404,1405],{"class":62},">> {\n",[32,1407,1409,1411,1413,1415,1417,1419,1421],{"class":34,"line":1408},43,[32,1410,451],{"class":58},[32,1412,454],{"class":62},[32,1414,66],{"class":58},[32,1416,69],{"class":58},[32,1418,443],{"class":72},[32,1420,76],{"class":58},[32,1422,1423],{"class":62},"[];\n",[32,1425,1427],{"class":34,"line":1426},44,[32,1428,52],{"emptyLinePlaceholder":51},[32,1430,1432,1434,1436,1438,1440],{"class":34,"line":1431},45,[32,1433,477],{"class":58},[32,1435,1002],{"class":102},[32,1437,216],{"class":62},[32,1439,443],{"class":72},[32,1441,1442],{"class":62}," item) {\n",[32,1444,1446,1449,1451],{"class":34,"line":1445},46,[32,1447,1448],{"class":62},"    _items.",[32,1450,496],{"class":102},[32,1452,1017],{"class":62},[32,1454,1456,1458,1461,1464,1466,1469,1472,1475],{"class":34,"line":1455},47,[32,1457,1448],{"class":62},[32,1459,1460],{"class":102},"sort",[32,1462,1463],{"class":62},"((a, b) ",[32,1465,490],{"class":58},[32,1467,1468],{"class":62}," a.",[32,1470,1471],{"class":102},"compareTo",[32,1473,1474],{"class":62},"(b));  ",[32,1476,1477],{"class":38},"\u002F\u002F ✓ compareTo available\n",[32,1479,1481],{"class":34,"line":1480},48,[32,1482,408],{"class":62},[32,1484,1486],{"class":34,"line":1485},49,[32,1487,52],{"emptyLinePlaceholder":51},[32,1489,1491,1493,1495,1497,1499,1502,1505,1507,1509,1511,1514],{"class":34,"line":1490},50,[32,1492,647],{"class":72},[32,1494,126],{"class":62},[32,1496,443],{"class":72},[32,1498,655],{"class":62},[32,1500,1501],{"class":58},"get",[32,1503,1504],{"class":62}," items ",[32,1506,490],{"class":58},[32,1508,123],{"class":72},[32,1510,274],{"class":62},[32,1512,1513],{"class":102},"unmodifiable",[32,1515,1516],{"class":62},"(_items);\n",[32,1518,1520],{"class":34,"line":1519},51,[32,1521,414],{"class":62},[32,1523,1525],{"class":34,"line":1524},52,[32,1526,52],{"emptyLinePlaceholder":51},[32,1528,1530],{"class":34,"line":1529},53,[32,1531,1532],{"class":38},"\u002F\u002F `T extends Object` — excludes Null (default bound is Object?).\n",[32,1534,1536,1538,1541,1543,1545,1547,1550],{"class":34,"line":1535},54,[32,1537,435],{"class":58},[32,1539,1540],{"class":72}," Cache",[32,1542,126],{"class":62},[32,1544,443],{"class":72},[32,1546,969],{"class":58},[32,1548,1549],{"class":72}," Object",[32,1551,446],{"class":62},[32,1553,1555,1557,1560,1562,1564,1566,1568,1570,1572],{"class":34,"line":1554},55,[32,1556,451],{"class":58},[32,1558,1559],{"class":62}," _cache ",[32,1561,66],{"class":58},[32,1563,69],{"class":58},[32,1565,171],{"class":72},[32,1567,85],{"class":62},[32,1569,443],{"class":72},[32,1571,76],{"class":58},[32,1573,1574],{"class":62},"{};\n",[32,1576,1578,1580,1582,1584,1586,1588,1591,1593,1596],{"class":34,"line":1577},56,[32,1579,511],{"class":72},[32,1581,1033],{"class":58},[32,1583,610],{"class":102},[32,1585,216],{"class":62},[32,1587,171],{"class":72},[32,1589,1590],{"class":62}," key) ",[32,1592,490],{"class":58},[32,1594,1595],{"class":62}," _cache[key];  ",[32,1597,1598],{"class":38},"\u002F\u002F T is non-nullable (excludes Null)\n",[32,1600,1602,1604,1607,1609,1611,1614,1616,1619,1621,1624,1626],{"class":34,"line":1601},57,[32,1603,477],{"class":58},[32,1605,1606],{"class":102}," set",[32,1608,216],{"class":62},[32,1610,171],{"class":72},[32,1612,1613],{"class":62}," key, ",[32,1615,443],{"class":72},[32,1617,1618],{"class":62}," value) ",[32,1620,490],{"class":58},[32,1622,1623],{"class":62}," _cache[key] ",[32,1625,66],{"class":58},[32,1627,1628],{"class":62}," value;\n",[32,1630,1632],{"class":34,"line":1631},58,[32,1633,414],{"class":62},[14,1635,1637],{"id":1636},"covariance-convenience-vs-soundness","Covariance — Convenience vs Soundness",[19,1639,1640,1940,1945,2059,2063,2533,2540,2873,2877,2984,2988,3175,3179,3183,3352,3355],{"language":21},[23,1641,1643],{"className":25,"code":1642,"language":21,"meta":27,"style":27},"\u002F\u002F Dart generics are COVARIANT: List\u003CDog> is a subtype of List\u003CAnimal>.\n\u002F\u002F This is convenient but UNSOUND — you could add a Cat to a List\u003CDog>\n\u002F\u002F passed as List\u003CAnimal>. The runtime catches violations (TypeError).\n\nclass Animal { String get name => 'Animal'; }\nclass Dog extends Animal { @override String get name => 'Dog'; }\nclass Cat extends Animal { @override String get name => 'Cat'; }\n\nvoid feedAll(List\u003CAnimal> animals) {\n  for (var a in animals) print('Feeding ${a.name}');\n}\n\nvar dogs = \u003CDog>[Dog(), Dog()];\nfeedAll(dogs);  \u002F\u002F ✓ List\u003CDog> is a List\u003CAnimal> (covariance)\n\n\u002F\u002F The soundness hole:\nvoid addCat(List\u003CAnimal> animals) {\n  animals.add(Cat());  \u002F\u002F ✓ compiles — List\u003CAnimal> accepts Animal\n}\n\nvar dogList = \u003CDog>[Dog()];\n\u002F\u002F addCat(dogList);  \u002F\u002F ✗ runtime TypeError: Cat is not a Dog\n\u002F\u002F At runtime, the VM checks the actual type (List\u003CDog>) and rejects Cat.\n",[29,1644,1645,1650,1655,1660,1664,1689,1718,1746,1750,1769,1808,1812,1816,1844,1855,1859,1864,1881,1898,1902,1906,1927,1935],{"__ignoreMap":27},[32,1646,1647],{"class":34,"line":35},[32,1648,1649],{"class":38},"\u002F\u002F Dart generics are COVARIANT: List\u003CDog> is a subtype of List\u003CAnimal>.\n",[32,1651,1652],{"class":34,"line":42},[32,1653,1654],{"class":38},"\u002F\u002F This is convenient but UNSOUND — you could add a Cat to a List\u003CDog>\n",[32,1656,1657],{"class":34,"line":48},[32,1658,1659],{"class":38},"\u002F\u002F passed as List\u003CAnimal>. The runtime catches violations (TypeError).\n",[32,1661,1662],{"class":34,"line":55},[32,1663,52],{"emptyLinePlaceholder":51},[32,1665,1666,1668,1671,1674,1676,1678,1681,1683,1686],{"class":34,"line":99},[32,1667,435],{"class":58},[32,1669,1670],{"class":72}," Animal",[32,1672,1673],{"class":62}," { ",[32,1675,171],{"class":72},[32,1677,610],{"class":58},[32,1679,1680],{"class":62}," name ",[32,1682,490],{"class":58},[32,1684,1685],{"class":264}," 'Animal'",[32,1687,1688],{"class":62},"; }\n",[32,1690,1691,1693,1696,1698,1700,1702,1705,1707,1709,1711,1713,1716],{"class":34,"line":112},[32,1692,435],{"class":58},[32,1694,1695],{"class":72}," Dog",[32,1697,969],{"class":58},[32,1699,1670],{"class":72},[32,1701,1673],{"class":62},[32,1703,1704],{"class":58},"@override",[32,1706,1204],{"class":72},[32,1708,610],{"class":58},[32,1710,1680],{"class":62},[32,1712,490],{"class":58},[32,1714,1715],{"class":264}," 'Dog'",[32,1717,1688],{"class":62},[32,1719,1720,1722,1725,1727,1729,1731,1733,1735,1737,1739,1741,1744],{"class":34,"line":137},[32,1721,435],{"class":58},[32,1723,1724],{"class":72}," Cat",[32,1726,969],{"class":58},[32,1728,1670],{"class":72},[32,1730,1673],{"class":62},[32,1732,1704],{"class":58},[32,1734,1204],{"class":72},[32,1736,610],{"class":58},[32,1738,1680],{"class":62},[32,1740,490],{"class":58},[32,1742,1743],{"class":264}," 'Cat'",[32,1745,1688],{"class":62},[32,1747,1748],{"class":34,"line":158},[32,1749,52],{"emptyLinePlaceholder":51},[32,1751,1752,1754,1757,1759,1761,1763,1766],{"class":34,"line":179},[32,1753,210],{"class":58},[32,1755,1756],{"class":102}," feedAll",[32,1758,216],{"class":62},[32,1760,219],{"class":72},[32,1762,126],{"class":62},[32,1764,1765],{"class":72},"Animal",[32,1767,1768],{"class":62},"> animals) {\n",[32,1770,1771,1774,1776,1778,1781,1783,1786,1788,1790,1793,1795,1797,1799,1802,1804,1806],{"class":34,"line":184},[32,1772,1773],{"class":58},"  for",[32,1775,1051],{"class":62},[32,1777,59],{"class":58},[32,1779,1780],{"class":62}," a ",[32,1782,1058],{"class":58},[32,1784,1785],{"class":62}," animals) ",[32,1787,103],{"class":102},[32,1789,216],{"class":62},[32,1791,1792],{"class":264},"'Feeding ",[32,1794,268],{"class":264},[32,1796,288],{"class":72},[32,1798,274],{"class":264},[32,1800,1801],{"class":72},"name",[32,1803,398],{"class":264},[32,1805,309],{"class":264},[32,1807,312],{"class":62},[32,1809,1810],{"class":34,"line":190},[32,1811,414],{"class":62},[32,1813,1814],{"class":34,"line":196},[32,1815,52],{"emptyLinePlaceholder":51},[32,1817,1818,1820,1823,1825,1827,1830,1832,1834,1836,1839,1841],{"class":34,"line":201},[32,1819,59],{"class":58},[32,1821,1822],{"class":62}," dogs ",[32,1824,66],{"class":58},[32,1826,69],{"class":58},[32,1828,1829],{"class":72},"Dog",[32,1831,76],{"class":58},[32,1833,79],{"class":62},[32,1835,1829],{"class":72},[32,1837,1838],{"class":62},"(), ",[32,1840,1829],{"class":72},[32,1842,1843],{"class":62},"()];\n",[32,1845,1846,1849,1852],{"class":34,"line":207},[32,1847,1848],{"class":102},"feedAll",[32,1850,1851],{"class":62},"(dogs);  ",[32,1853,1854],{"class":38},"\u002F\u002F ✓ List\u003CDog> is a List\u003CAnimal> (covariance)\n",[32,1856,1857],{"class":34,"line":230},[32,1858,52],{"emptyLinePlaceholder":51},[32,1860,1861],{"class":34,"line":250},[32,1862,1863],{"class":38},"\u002F\u002F The soundness hole:\n",[32,1865,1866,1868,1871,1873,1875,1877,1879],{"class":34,"line":256},[32,1867,210],{"class":58},[32,1869,1870],{"class":102}," addCat",[32,1872,216],{"class":62},[32,1874,219],{"class":72},[32,1876,126],{"class":62},[32,1878,1765],{"class":72},[32,1880,1768],{"class":62},[32,1882,1883,1886,1888,1890,1893,1895],{"class":34,"line":315},[32,1884,1885],{"class":62},"  animals.",[32,1887,496],{"class":102},[32,1889,216],{"class":62},[32,1891,1892],{"class":72},"Cat",[32,1894,877],{"class":62},[32,1896,1897],{"class":38},"\u002F\u002F ✓ compiles — List\u003CAnimal> accepts Animal\n",[32,1899,1900],{"class":34,"line":339},[32,1901,414],{"class":62},[32,1903,1904],{"class":34,"line":369},[32,1905,52],{"emptyLinePlaceholder":51},[32,1907,1908,1910,1913,1915,1917,1919,1921,1923,1925],{"class":34,"line":379},[32,1909,59],{"class":58},[32,1911,1912],{"class":62}," dogList ",[32,1914,66],{"class":58},[32,1916,69],{"class":58},[32,1918,1829],{"class":72},[32,1920,76],{"class":58},[32,1922,79],{"class":62},[32,1924,1829],{"class":72},[32,1926,1843],{"class":62},[32,1928,1929,1932],{"class":34,"line":405},[32,1930,1931],{"class":38},"\u002F\u002F addCat(dogList);",[32,1933,1934],{"class":38},"  \u002F\u002F ✗ runtime TypeError: Cat is not a Dog\n",[32,1936,1937],{"class":34,"line":411},[32,1938,1939],{"class":38},"\u002F\u002F At runtime, the VM checks the actual type (List\u003CDog>) and rejects Cat.\n",[1941,1942,1944],"h3",{"id":1943},"covariance-anti-pattern","Covariance Anti-Pattern",[19,1946,1947],{"language":21},[23,1948,1950],{"className":25,"code":1949,"language":21,"meta":27,"style":27},"\u002F\u002F ❌ Anti-pattern: relying on covariance to add to a list.\nvoid addToDogs(List\u003CAnimal> animals) {\n  animals.add(Dog());  \u002F\u002F seems fine — but if `animals` is actually List\u003CCat>...\n}\n\nvar cats = \u003CCat>[Cat()];\n\u002F\u002F addToDogs(cats);  \u002F\u002F ✗ runtime TypeError: Dog is not a Cat\n\n\u002F\u002F The runtime check catches it, but it's a design smell. If you're adding,\n\u002F\u002F the parameter should be typed correctly. Covariance is safe for READS,\n\u002F\u002F not WRITES.\n\n\u002F\u002F ✓ For write-safe APIs, use `List\u003CAnimal>` (accepts any Animal subtype list\n\u002F\u002F for reading). For writes, accept `List\u003CAnimal>` explicitly constructed.\n",[29,1951,1952,1957,1974,1989,1993,1997,2018,2026,2030,2035,2040,2045,2049,2054],{"__ignoreMap":27},[32,1953,1954],{"class":34,"line":35},[32,1955,1956],{"class":38},"\u002F\u002F ❌ Anti-pattern: relying on covariance to add to a list.\n",[32,1958,1959,1961,1964,1966,1968,1970,1972],{"class":34,"line":42},[32,1960,210],{"class":58},[32,1962,1963],{"class":102}," addToDogs",[32,1965,216],{"class":62},[32,1967,219],{"class":72},[32,1969,126],{"class":62},[32,1971,1765],{"class":72},[32,1973,1768],{"class":62},[32,1975,1976,1978,1980,1982,1984,1986],{"class":34,"line":48},[32,1977,1885],{"class":62},[32,1979,496],{"class":102},[32,1981,216],{"class":62},[32,1983,1829],{"class":72},[32,1985,877],{"class":62},[32,1987,1988],{"class":38},"\u002F\u002F seems fine — but if `animals` is actually List\u003CCat>...\n",[32,1990,1991],{"class":34,"line":55},[32,1992,414],{"class":62},[32,1994,1995],{"class":34,"line":99},[32,1996,52],{"emptyLinePlaceholder":51},[32,1998,1999,2001,2004,2006,2008,2010,2012,2014,2016],{"class":34,"line":112},[32,2000,59],{"class":58},[32,2002,2003],{"class":62}," cats ",[32,2005,66],{"class":58},[32,2007,69],{"class":58},[32,2009,1892],{"class":72},[32,2011,76],{"class":58},[32,2013,79],{"class":62},[32,2015,1892],{"class":72},[32,2017,1843],{"class":62},[32,2019,2020,2023],{"class":34,"line":137},[32,2021,2022],{"class":38},"\u002F\u002F addToDogs(cats);",[32,2024,2025],{"class":38},"  \u002F\u002F ✗ runtime TypeError: Dog is not a Cat\n",[32,2027,2028],{"class":34,"line":158},[32,2029,52],{"emptyLinePlaceholder":51},[32,2031,2032],{"class":34,"line":179},[32,2033,2034],{"class":38},"\u002F\u002F The runtime check catches it, but it's a design smell. If you're adding,\n",[32,2036,2037],{"class":34,"line":184},[32,2038,2039],{"class":38},"\u002F\u002F the parameter should be typed correctly. Covariance is safe for READS,\n",[32,2041,2042],{"class":34,"line":190},[32,2043,2044],{"class":38},"\u002F\u002F not WRITES.\n",[32,2046,2047],{"class":34,"line":196},[32,2048,52],{"emptyLinePlaceholder":51},[32,2050,2051],{"class":34,"line":201},[32,2052,2053],{"class":38},"\u002F\u002F ✓ For write-safe APIs, use `List\u003CAnimal>` (accepts any Animal subtype list\n",[32,2055,2056],{"class":34,"line":207},[32,2057,2058],{"class":38},"\u002F\u002F for reading). For writes, accept `List\u003CAnimal>` explicitly constructed.\n",[14,2060,2062],{"id":2061},"generic-methods-independent-type-parameters","Generic Methods — Independent Type Parameters",[19,2064,2065],{"language":21},[23,2066,2068],{"className":25,"code":2067,"language":21,"meta":27,"style":27},"\u002F\u002F A generic method has its own type parameter, independent of the class.\nclass Cache {\n  final _cache = \u003CString, Object>{};\n\n  \u002F\u002F T is inferred from the `loader` return type at the call site.\n  T getOrCompute\u003CT extends Object>(String key, T Function() loader) {\n    final cached = _cache[key];\n    if (cached is T) return cached;  \u002F\u002F reified check — safe\n    final value = loader();\n    _cache[key] = value;\n    return value;\n  }\n}\n\nfinal cache = Cache();\nvar user = cache.getOrCompute('user', () => fetchUser());  \u002F\u002F T = User\nvar count = cache.getOrCompute('count', () => 42);  \u002F\u002F T = int\n\n\u002F\u002F ❌ Anti-pattern: `as T` blindly trusts the caller's type parameter.\nclass BadCache {\n  final _cache = \u003CString, Object>{};\n  T get\u003CT>(String key) => _cache[key] as T;  \u002F\u002F throws TypeError if wrong type\n}\n\nvoid main() {\n  final c = BadCache();\n  c._cache['x'] = 42;\n  \u002F\u002F c.get\u003CString>('x');  \u002F\u002F ✗ TypeError: 42 is not a String\n}\n\n\u002F\u002F ✓ Correct: use `is T` (reified check) to validate before returning.\nclass SafeCache {\n  final _cache = \u003CString, Object>{};\n  T? get\u003CT extends Object>(String key) {\n    final value = _cache[key];\n    if (value is T) return value;  \u002F\u002F reified generics enable this\n    return null;  \u002F\u002F type mismatch or missing — no crash\n  }\n}\n",[29,2069,2070,2075,2083,2104,2108,2113,2141,2154,2177,2191,2200,2206,2210,2214,2218,2232,2265,2294,2298,2303,2312,2332,2363,2367,2371,2380,2393,2410,2418,2422,2426,2431,2440,2460,2483,2493,2514,2525,2529],{"__ignoreMap":27},[32,2071,2072],{"class":34,"line":35},[32,2073,2074],{"class":38},"\u002F\u002F A generic method has its own type parameter, independent of the class.\n",[32,2076,2077,2079,2081],{"class":34,"line":42},[32,2078,435],{"class":58},[32,2080,1540],{"class":72},[32,2082,376],{"class":62},[32,2084,2085,2087,2089,2091,2093,2095,2097,2100,2102],{"class":34,"line":48},[32,2086,451],{"class":58},[32,2088,1559],{"class":62},[32,2090,66],{"class":58},[32,2092,69],{"class":58},[32,2094,171],{"class":72},[32,2096,85],{"class":62},[32,2098,2099],{"class":72},"Object",[32,2101,76],{"class":58},[32,2103,1574],{"class":62},[32,2105,2106],{"class":34,"line":55},[32,2107,52],{"emptyLinePlaceholder":51},[32,2109,2110],{"class":34,"line":99},[32,2111,2112],{"class":38},"  \u002F\u002F T is inferred from the `loader` return type at the call site.\n",[32,2114,2115,2117,2120,2122,2124,2126,2128,2130,2132,2134,2136,2138],{"class":34,"line":112},[32,2116,511],{"class":72},[32,2118,2119],{"class":102}," getOrCompute",[32,2121,126],{"class":62},[32,2123,443],{"class":72},[32,2125,969],{"class":58},[32,2127,1549],{"class":72},[32,2129,665],{"class":62},[32,2131,171],{"class":72},[32,2133,1613],{"class":62},[32,2135,443],{"class":72},[32,2137,670],{"class":72},[32,2139,2140],{"class":62},"() loader) {\n",[32,2142,2143,2146,2149,2151],{"class":34,"line":137},[32,2144,2145],{"class":58},"    final",[32,2147,2148],{"class":62}," cached ",[32,2150,66],{"class":58},[32,2152,2153],{"class":62}," _cache[key];\n",[32,2155,2156,2158,2161,2163,2166,2169,2171,2174],{"class":34,"line":158},[32,2157,522],{"class":58},[32,2159,2160],{"class":62}," (cached ",[32,2162,120],{"class":58},[32,2164,2165],{"class":72}," T",[32,2167,2168],{"class":62},") ",[32,2170,1078],{"class":58},[32,2172,2173],{"class":62}," cached;  ",[32,2175,2176],{"class":38},"\u002F\u002F reified check — safe\n",[32,2178,2179,2181,2184,2186,2189],{"class":34,"line":179},[32,2180,2145],{"class":58},[32,2182,2183],{"class":62}," value ",[32,2185,66],{"class":58},[32,2187,2188],{"class":102}," loader",[32,2190,692],{"class":62},[32,2192,2193,2196,2198],{"class":34,"line":184},[32,2194,2195],{"class":62},"    _cache[key] ",[32,2197,66],{"class":58},[32,2199,1628],{"class":62},[32,2201,2202,2204],{"class":34,"line":190},[32,2203,543],{"class":58},[32,2205,1628],{"class":62},[32,2207,2208],{"class":34,"line":196},[32,2209,408],{"class":62},[32,2211,2212],{"class":34,"line":201},[32,2213,414],{"class":62},[32,2215,2216],{"class":34,"line":207},[32,2217,52],{"emptyLinePlaceholder":51},[32,2219,2220,2223,2226,2228,2230],{"class":34,"line":230},[32,2221,2222],{"class":58},"final",[32,2224,2225],{"class":62}," cache ",[32,2227,66],{"class":58},[32,2229,1540],{"class":72},[32,2231,692],{"class":62},[32,2233,2234,2236,2239,2241,2244,2247,2249,2252,2255,2257,2260,2262],{"class":34,"line":250},[32,2235,59],{"class":58},[32,2237,2238],{"class":62}," user ",[32,2240,66],{"class":58},[32,2242,2243],{"class":62}," cache.",[32,2245,2246],{"class":102},"getOrCompute",[32,2248,216],{"class":62},[32,2250,2251],{"class":264},"'user'",[32,2253,2254],{"class":62},", () ",[32,2256,490],{"class":58},[32,2258,2259],{"class":102}," fetchUser",[32,2261,877],{"class":62},[32,2263,2264],{"class":38},"\u002F\u002F T = User\n",[32,2266,2267,2269,2272,2274,2276,2278,2280,2283,2285,2287,2290,2292],{"class":34,"line":256},[32,2268,59],{"class":58},[32,2270,2271],{"class":62}," count ",[32,2273,66],{"class":58},[32,2275,2243],{"class":62},[32,2277,2246],{"class":102},[32,2279,216],{"class":62},[32,2281,2282],{"class":264},"'count'",[32,2284,2254],{"class":62},[32,2286,490],{"class":58},[32,2288,2289],{"class":72}," 42",[32,2291,847],{"class":62},[32,2293,800],{"class":38},[32,2295,2296],{"class":34,"line":315},[32,2297,52],{"emptyLinePlaceholder":51},[32,2299,2300],{"class":34,"line":339},[32,2301,2302],{"class":38},"\u002F\u002F ❌ Anti-pattern: `as T` blindly trusts the caller's type parameter.\n",[32,2304,2305,2307,2310],{"class":34,"line":369},[32,2306,435],{"class":58},[32,2308,2309],{"class":72}," BadCache",[32,2311,376],{"class":62},[32,2313,2314,2316,2318,2320,2322,2324,2326,2328,2330],{"class":34,"line":379},[32,2315,451],{"class":58},[32,2317,1559],{"class":62},[32,2319,66],{"class":58},[32,2321,69],{"class":58},[32,2323,171],{"class":72},[32,2325,85],{"class":62},[32,2327,2099],{"class":72},[32,2329,76],{"class":58},[32,2331,1574],{"class":62},[32,2333,2334,2336,2338,2340,2342,2344,2346,2348,2350,2352,2355,2357,2360],{"class":34,"line":405},[32,2335,511],{"class":72},[32,2337,610],{"class":102},[32,2339,126],{"class":62},[32,2341,443],{"class":72},[32,2343,665],{"class":62},[32,2345,171],{"class":72},[32,2347,1590],{"class":62},[32,2349,490],{"class":58},[32,2351,1623],{"class":62},[32,2353,2354],{"class":58},"as",[32,2356,2165],{"class":72},[32,2358,2359],{"class":62},";  ",[32,2361,2362],{"class":38},"\u002F\u002F throws TypeError if wrong type\n",[32,2364,2365],{"class":34,"line":411},[32,2366,414],{"class":62},[32,2368,2369],{"class":34,"line":703},[32,2370,52],{"emptyLinePlaceholder":51},[32,2372,2373,2375,2378],{"class":34,"line":709},[32,2374,210],{"class":58},[32,2376,2377],{"class":102}," main",[32,2379,517],{"class":62},[32,2381,2382,2384,2387,2389,2391],{"class":34,"line":731},[32,2383,451],{"class":58},[32,2385,2386],{"class":62}," c ",[32,2388,66],{"class":58},[32,2390,2309],{"class":72},[32,2392,692],{"class":62},[32,2394,2395,2398,2401,2404,2406,2408],{"class":34,"line":747},[32,2396,2397],{"class":62},"  c._cache[",[32,2399,2400],{"class":264},"'x'",[32,2402,2403],{"class":62},"] ",[32,2405,66],{"class":58},[32,2407,2289],{"class":72},[32,2409,1099],{"class":62},[32,2411,2412,2415],{"class":34,"line":768},[32,2413,2414],{"class":38},"  \u002F\u002F c.get\u003CString>('x');",[32,2416,2417],{"class":38},"  \u002F\u002F ✗ TypeError: 42 is not a String\n",[32,2419,2420],{"class":34,"line":777},[32,2421,414],{"class":62},[32,2423,2424],{"class":34,"line":782},[32,2425,52],{"emptyLinePlaceholder":51},[32,2427,2428],{"class":34,"line":803},[32,2429,2430],{"class":38},"\u002F\u002F ✓ Correct: use `is T` (reified check) to validate before returning.\n",[32,2432,2433,2435,2438],{"class":34,"line":818},[32,2434,435],{"class":58},[32,2436,2437],{"class":72}," SafeCache",[32,2439,376],{"class":62},[32,2441,2442,2444,2446,2448,2450,2452,2454,2456,2458],{"class":34,"line":853},[32,2443,451],{"class":58},[32,2445,1559],{"class":62},[32,2447,66],{"class":58},[32,2449,69],{"class":58},[32,2451,171],{"class":72},[32,2453,85],{"class":62},[32,2455,2099],{"class":72},[32,2457,76],{"class":58},[32,2459,1574],{"class":62},[32,2461,2462,2464,2466,2468,2470,2472,2474,2476,2478,2480],{"class":34,"line":1281},[32,2463,511],{"class":72},[32,2465,1033],{"class":58},[32,2467,610],{"class":102},[32,2469,126],{"class":62},[32,2471,443],{"class":72},[32,2473,969],{"class":58},[32,2475,1549],{"class":72},[32,2477,665],{"class":62},[32,2479,171],{"class":72},[32,2481,2482],{"class":62}," key) {\n",[32,2484,2485,2487,2489,2491],{"class":34,"line":1286},[32,2486,2145],{"class":58},[32,2488,2183],{"class":62},[32,2490,66],{"class":58},[32,2492,2153],{"class":62},[32,2494,2495,2497,2500,2502,2504,2506,2508,2511],{"class":34,"line":1308},[32,2496,522],{"class":58},[32,2498,2499],{"class":62}," (value ",[32,2501,120],{"class":58},[32,2503,2165],{"class":72},[32,2505,2168],{"class":62},[32,2507,1078],{"class":58},[32,2509,2510],{"class":62}," value;  ",[32,2512,2513],{"class":38},"\u002F\u002F reified generics enable this\n",[32,2515,2516,2518,2520,2522],{"class":34,"line":1317},[32,2517,543],{"class":58},[32,2519,1096],{"class":72},[32,2521,2359],{"class":62},[32,2523,2524],{"class":38},"\u002F\u002F type mismatch or missing — no crash\n",[32,2526,2527],{"class":34,"line":1341},[32,2528,408],{"class":62},[32,2530,2531],{"class":34,"line":1367},[32,2532,414],{"class":62},[14,2534,2536,2537,2539],{"id":2535},"type-inference-dynamic-traps","Type Inference & ",[29,2538,224],{}," Traps",[19,2541,2542],{"language":21},[23,2543,2545],{"className":25,"code":2544,"language":21,"meta":27,"style":27},"\u002F\u002F Type inference from arguments:\nT firstOf\u003CT>(List\u003CT> items) => items.first;\n\nvar x = firstOf([1, 2, 3]);  \u002F\u002F T inferred as int — x is int\nvar y = firstOf(['a', 'b']);  \u002F\u002F T inferred as String — y is String\nvar z = firstOf\u003Cint>([1, 2]);  \u002F\u002F explicit T — z is int\n\n\u002F\u002F ❌ Anti-pattern: dynamic list causes T to infer as dynamic.\nvar mixed = \u003Cdynamic>[1, 'two', 3.0];\nvar first = firstOf(mixed);  \u002F\u002F T = dynamic — first is dynamic, no type safety\nfirst.abs();  \u002F\u002F compiles (dynamic), throws NoSuchMethodError on 'two'\n\n\u002F\u002F ✓ Correct: use typed lists, or convert before passing.\nvar typed = mixed.whereType\u003Cint>().toList();  \u002F\u002F filters to List\u003Cint>\nvar firstInt = firstOf(typed);  \u002F\u002F T = int\n\n\u002F\u002F Generic type parameters are not values — you can't use `T` as a Type:\nclass Box\u003CT> {\n  \u002F\u002F print(T);  \u002F\u002F ✗ T is not a value, it's a type\n  Type get type => T;  \u002F\u002F ✓ T can be used as a Type in type context\n  bool isTypeOf(Object obj) => obj is T;  \u002F\u002F ✓ reified check\n}\n",[29,2546,2547,2552,2579,2583,2613,2639,2668,2672,2677,2708,2725,2738,2742,2747,2776,2792,2796,2801,2814,2822,2841,2869],{"__ignoreMap":27},[32,2548,2549],{"class":34,"line":35},[32,2550,2551],{"class":38},"\u002F\u002F Type inference from arguments:\n",[32,2553,2554,2556,2559,2561,2563,2565,2567,2569,2571,2574,2576],{"class":34,"line":42},[32,2555,443],{"class":72},[32,2557,2558],{"class":102}," firstOf",[32,2560,126],{"class":62},[32,2562,443],{"class":72},[32,2564,665],{"class":62},[32,2566,219],{"class":72},[32,2568,126],{"class":62},[32,2570,443],{"class":72},[32,2572,2573],{"class":62},"> items) ",[32,2575,490],{"class":58},[32,2577,2578],{"class":62}," items.first;\n",[32,2580,2581],{"class":34,"line":48},[32,2582,52],{"emptyLinePlaceholder":51},[32,2584,2585,2587,2590,2592,2594,2597,2599,2601,2603,2605,2607,2610],{"class":34,"line":55},[32,2586,59],{"class":58},[32,2588,2589],{"class":62}," x ",[32,2591,66],{"class":58},[32,2593,2558],{"class":102},[32,2595,2596],{"class":62},"([",[32,2598,82],{"class":72},[32,2600,85],{"class":62},[32,2602,88],{"class":72},[32,2604,85],{"class":62},[32,2606,93],{"class":72},[32,2608,2609],{"class":62},"]);  ",[32,2611,2612],{"class":38},"\u002F\u002F T inferred as int — x is int\n",[32,2614,2615,2617,2620,2622,2624,2626,2629,2631,2634,2636],{"class":34,"line":99},[32,2616,59],{"class":58},[32,2618,2619],{"class":62}," y ",[32,2621,66],{"class":58},[32,2623,2558],{"class":102},[32,2625,2596],{"class":62},[32,2627,2628],{"class":264},"'a'",[32,2630,85],{"class":62},[32,2632,2633],{"class":264},"'b'",[32,2635,2609],{"class":62},[32,2637,2638],{"class":38},"\u002F\u002F T inferred as String — y is String\n",[32,2640,2641,2643,2646,2648,2650,2652,2654,2657,2659,2661,2663,2665],{"class":34,"line":112},[32,2642,59],{"class":58},[32,2644,2645],{"class":62}," z ",[32,2647,66],{"class":58},[32,2649,2558],{"class":102},[32,2651,126],{"class":62},[32,2653,73],{"class":72},[32,2655,2656],{"class":62},">([",[32,2658,82],{"class":72},[32,2660,85],{"class":62},[32,2662,88],{"class":72},[32,2664,2609],{"class":62},[32,2666,2667],{"class":38},"\u002F\u002F explicit T — z is int\n",[32,2669,2670],{"class":34,"line":137},[32,2671,52],{"emptyLinePlaceholder":51},[32,2673,2674],{"class":34,"line":158},[32,2675,2676],{"class":38},"\u002F\u002F ❌ Anti-pattern: dynamic list causes T to infer as dynamic.\n",[32,2678,2679,2681,2684,2686,2688,2690,2692,2694,2696,2698,2701,2703,2706],{"class":34,"line":179},[32,2680,59],{"class":58},[32,2682,2683],{"class":62}," mixed ",[32,2685,66],{"class":58},[32,2687,69],{"class":58},[32,2689,224],{"class":72},[32,2691,76],{"class":58},[32,2693,79],{"class":62},[32,2695,82],{"class":72},[32,2697,85],{"class":62},[32,2699,2700],{"class":264},"'two'",[32,2702,85],{"class":62},[32,2704,2705],{"class":72},"3.0",[32,2707,96],{"class":62},[32,2709,2710,2712,2715,2717,2719,2722],{"class":34,"line":184},[32,2711,59],{"class":58},[32,2713,2714],{"class":62}," first ",[32,2716,66],{"class":58},[32,2718,2558],{"class":102},[32,2720,2721],{"class":62},"(mixed);  ",[32,2723,2724],{"class":38},"\u002F\u002F T = dynamic — first is dynamic, no type safety\n",[32,2726,2727,2730,2733,2735],{"class":34,"line":190},[32,2728,2729],{"class":62},"first.",[32,2731,2732],{"class":102},"abs",[32,2734,551],{"class":62},[32,2736,2737],{"class":38},"\u002F\u002F compiles (dynamic), throws NoSuchMethodError on 'two'\n",[32,2739,2740],{"class":34,"line":196},[32,2741,52],{"emptyLinePlaceholder":51},[32,2743,2744],{"class":34,"line":201},[32,2745,2746],{"class":38},"\u002F\u002F ✓ Correct: use typed lists, or convert before passing.\n",[32,2748,2749,2751,2754,2756,2759,2762,2764,2766,2769,2771,2773],{"class":34,"line":207},[32,2750,59],{"class":58},[32,2752,2753],{"class":62}," typed ",[32,2755,66],{"class":58},[32,2757,2758],{"class":62}," mixed.",[32,2760,2761],{"class":102},"whereType",[32,2763,126],{"class":62},[32,2765,73],{"class":72},[32,2767,2768],{"class":62},">().",[32,2770,689],{"class":102},[32,2772,551],{"class":62},[32,2774,2775],{"class":38},"\u002F\u002F filters to List\u003Cint>\n",[32,2777,2778,2780,2783,2785,2787,2790],{"class":34,"line":230},[32,2779,59],{"class":58},[32,2781,2782],{"class":62}," firstInt ",[32,2784,66],{"class":58},[32,2786,2558],{"class":102},[32,2788,2789],{"class":62},"(typed);  ",[32,2791,800],{"class":38},[32,2793,2794],{"class":34,"line":250},[32,2795,52],{"emptyLinePlaceholder":51},[32,2797,2798],{"class":34,"line":256},[32,2799,2800],{"class":38},"\u002F\u002F Generic type parameters are not values — you can't use `T` as a Type:\n",[32,2802,2803,2805,2808,2810,2812],{"class":34,"line":315},[32,2804,435],{"class":58},[32,2806,2807],{"class":72}," Box",[32,2809,126],{"class":62},[32,2811,443],{"class":72},[32,2813,446],{"class":62},[32,2815,2816,2819],{"class":34,"line":339},[32,2817,2818],{"class":38},"  \u002F\u002F print(T);",[32,2820,2821],{"class":38},"  \u002F\u002F ✗ T is not a value, it's a type\n",[32,2823,2824,2827,2829,2832,2834,2836,2838],{"class":34,"line":369},[32,2825,2826],{"class":72},"  Type",[32,2828,610],{"class":58},[32,2830,2831],{"class":62}," type ",[32,2833,490],{"class":58},[32,2835,2165],{"class":72},[32,2837,2359],{"class":62},[32,2839,2840],{"class":38},"\u002F\u002F ✓ T can be used as a Type in type context\n",[32,2842,2843,2845,2848,2850,2852,2855,2857,2860,2862,2864,2866],{"class":34,"line":379},[32,2844,607],{"class":72},[32,2846,2847],{"class":102}," isTypeOf",[32,2849,216],{"class":62},[32,2851,2099],{"class":72},[32,2853,2854],{"class":62}," obj) ",[32,2856,490],{"class":58},[32,2858,2859],{"class":62}," obj ",[32,2861,120],{"class":58},[32,2863,2165],{"class":72},[32,2865,2359],{"class":62},[32,2867,2868],{"class":38},"\u002F\u002F ✓ reified check\n",[32,2870,2871],{"class":34,"line":405},[32,2872,414],{"class":62},[14,2874,2876],{"id":2875},"tips-tricks","💡 Tips & Tricks",[2878,2879,2880,2911,2934,2958,2975],"ul",{},[2881,2882,2883,2887,2888,2891,2892,2895,2896,2899,2900,2902,2903,2906,2907,2910],"li",{},[2884,2885,2886],"strong",{},"Idiom",": use ",[29,2889,2890],{},"is T"," (not ",[29,2893,2894],{},"as T",") for generic cache retrieval — ",[29,2897,2898],{},"if (value is T) return value"," uses reified generics for a safe runtime check. ",[29,2901,2894],{}," blindly trusts the caller and throws ",[29,2904,2905],{},"TypeError"," on mismatch. Return ",[29,2908,2909],{},"null"," (or a Result) on mismatch instead of crashing.",[2881,2912,2913,2915,2916,2919,2920,2923,2924,2927,2928,2930,2931,2933],{},[2884,2914,2886],{},": ",[29,2917,2918],{},"T extends Object"," to exclude ",[29,2921,2922],{},"Null"," from a type parameter — the default bound is ",[29,2925,2926],{},"Object?"," (nullable). ",[29,2929,2918],{}," makes ",[29,2932,443],{}," non-nullable, useful for caches\u002Fcontainers that shouldn't hold null.",[2881,2935,2936,2938,2939,2942,2943,2946,2947,2950,2951,2953,2954,2957],{},[2884,2937,2886],{},": bounded type parameters (",[29,2940,2941],{},"T extends Model",") to access the bound's methods — inside ",[29,2944,2945],{},"Repository\u003CT extends Model>",", you can call ",[29,2948,2949],{},"Model","'s methods on ",[29,2952,443],{},". Use ",[29,2955,2956],{},"T extends Comparable\u003CT>"," for sortable types (self-bounded).",[2881,2959,2960,2962,2963,2966,2967,2970,2971,2974],{},[2884,2961,2886],{},": let type parameters be inferred when obvious — ",[29,2964,2965],{},"firstOf([1,2,3])"," infers ",[29,2968,2969],{},"T = int",". Explicit ",[29,2972,2973],{},"\u003Cint>"," is redundant. Specify when inference is unclear or for readability at the call site.",[2881,2976,2977,2979,2980,2983],{},[2884,2978,2886],{},": use reified generics for runtime type dispatch — ",[29,2981,2982],{},"if (x is List\u003Cint>)"," works at runtime (unlike Java's erased generics). Use for type-based processing, but prefer polymorphism when possible.",[14,2985,2987],{"id":2986},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2878,2989,2990,3007,3031,3051,3060,3076,3094,3111,3134,3151],{},[2881,2991,2992,2915,2995,2998,2999,3002,3003,3006],{},[2884,2993,2994],{},"Dart generics are reified",[29,2996,2997],{},"List\u003Cint>.runtimeType"," is ",[29,3000,3001],{},"List\u003Cint>","; ",[29,3004,3005],{},"is List\u003Cint>"," works at runtime. Unlike Java's erased generics, full type info is preserved.",[2881,3008,3009,2915,3012,2998,3015,3018,3019,3021,3022,3024,3025,3027,3028,3030],{},[2884,3010,3011],{},"Covariance is unsound but allowed",[29,3013,3014],{},"List\u003CDog>",[29,3016,3017],{},"List\u003CAnimal>"," (covariant). Adding a ",[29,3020,1892],{}," to a ",[29,3023,3014],{}," passed as ",[29,3026,3017],{}," → runtime ",[29,3029,2905],{},". Dart trades soundness for ergonomics.",[2881,3032,3033,3041,3042,3021,3045,3047,3048,3050],{},[2884,3034,3035,2998,3037,3040],{},[29,3036,3001],{},[29,3038,3039],{},"List\u003Cnum>"," (covariance)",": but you can't add a ",[29,3043,3044],{},"double",[29,3046,3001],{}," (runtime check rejects). ",[29,3049,3039],{}," accepts both.",[2881,3052,3053,3056,3057,3059],{},[2884,3054,3055],{},"Generic type parameters can't be used in static contexts",": a static method can't reference the class's ",[29,3058,443],{}," (no instance). Use a generic method with its own type parameter.",[2881,3061,3062,2915,3067,3070,3071,3073,3074,274],{},[2884,3063,3064,3066],{},[29,3065,443],{}," is not a value",[29,3068,3069],{},"print(T)"," is invalid. Use ",[29,3072,443],{}," as a type annotation, not a value. For the runtime type of an instance, use ",[29,3075,395],{},[2881,3077,3078,2915,3083,3085,3086,3088,3089,2953,3091,3093],{},[2884,3079,3080,3081],{},"Default bound is ",[29,3082,2926],{},[29,3084,443],{}," without a bound is ",[29,3087,2926],{}," — can be ",[29,3090,2922],{},[29,3092,2918],{}," to exclude null.",[2881,3095,3096,2915,3101,3104,3105,3107,3108,3110],{},[2884,3097,3098,3100],{},[29,3099,2894],{}," in a generic cache throws on mismatch",[29,3102,3103],{},"_cache[key] as T"," blindly trusts the caller's type. Use ",[29,3106,2890],{}," (reified check) to validate, returning ",[29,3109,2909],{}," on mismatch.",[2881,3112,3113,2915,3123,2966,3126,3129,3130,3133],{},[2884,3114,3115,3117,3118,3120,3121],{},[29,3116,224],{}," lists cause ",[29,3119,443],{}," to infer as ",[29,3122,224],{},[29,3124,3125],{},"firstOf(\u003Cdynamic>[1, 'two'])",[29,3127,3128],{},"T = dynamic"," — no type safety. Convert to a typed list first (",[29,3131,3132],{},"whereType\u003Cint>()",").",[2881,3135,3136,3143,3144,3147,3148,3133],{},[2884,3137,3138,3139,3142],{},"No ",[29,3140,3141],{},"super"," bounds (lower bounds)",": Dart doesn't have ",[29,3145,3146],{},"? super T"," like Java. Only upper bounds (",[29,3149,3150],{},"T extends Animal",[2881,3152,3153,2915,3156,3159,3160,2998,3163,3166,3167,3170,3171,3174],{},[2884,3154,3155],{},"Casting generic collections",[29,3157,3158],{},"list as List\u003Cint>"," throws if ",[29,3161,3162],{},"list",[29,3164,3165],{},"List\u003Cdynamic>"," with non-ints. Use ",[29,3168,3169],{},"list.cast\u003Cint>()"," (lazy cast, checks on access) or ",[29,3172,3173],{},"List\u003Cint>.from(list)"," (eager copy).",[14,3176,3178],{"id":3177},"spot-the-bug","🧠 Spot the Bug",[3180,3181,3182],"p",{},"A developer creates a generic cache and retrieves a value with the wrong type:",[19,3184,3185],{"language":21},[23,3186,3188],{"className":25,"code":3187,"language":21,"meta":27,"style":27},"class Cache {\n  final _cache = \u003CString, Object>{};\n  void put(String key, Object value) => _cache[key] = value;\n  T get\u003CT>(String key) => _cache[key] as T;\n}\n\nvoid main() {\n  final cache = Cache();\n  cache.put('count', 42);\n  final name = cache.get\u003CString>('count');  \u002F\u002F 💥\n  print(name);\n}\n",[29,3189,3190,3198,3218,3243,3269,3273,3277,3285,3297,3315,3340,3348],{"__ignoreMap":27},[32,3191,3192,3194,3196],{"class":34,"line":35},[32,3193,435],{"class":58},[32,3195,1540],{"class":72},[32,3197,376],{"class":62},[32,3199,3200,3202,3204,3206,3208,3210,3212,3214,3216],{"class":34,"line":42},[32,3201,451],{"class":58},[32,3203,1559],{"class":62},[32,3205,66],{"class":58},[32,3207,69],{"class":58},[32,3209,171],{"class":72},[32,3211,85],{"class":62},[32,3213,2099],{"class":72},[32,3215,76],{"class":58},[32,3217,1574],{"class":62},[32,3219,3220,3222,3225,3227,3229,3231,3233,3235,3237,3239,3241],{"class":34,"line":48},[32,3221,477],{"class":58},[32,3223,3224],{"class":102}," put",[32,3226,216],{"class":62},[32,3228,171],{"class":72},[32,3230,1613],{"class":62},[32,3232,2099],{"class":72},[32,3234,1618],{"class":62},[32,3236,490],{"class":58},[32,3238,1623],{"class":62},[32,3240,66],{"class":58},[32,3242,1628],{"class":62},[32,3244,3245,3247,3249,3251,3253,3255,3257,3259,3261,3263,3265,3267],{"class":34,"line":55},[32,3246,511],{"class":72},[32,3248,610],{"class":102},[32,3250,126],{"class":62},[32,3252,443],{"class":72},[32,3254,665],{"class":62},[32,3256,171],{"class":72},[32,3258,1590],{"class":62},[32,3260,490],{"class":58},[32,3262,1623],{"class":62},[32,3264,2354],{"class":58},[32,3266,2165],{"class":72},[32,3268,1099],{"class":62},[32,3270,3271],{"class":34,"line":99},[32,3272,414],{"class":62},[32,3274,3275],{"class":34,"line":112},[32,3276,52],{"emptyLinePlaceholder":51},[32,3278,3279,3281,3283],{"class":34,"line":137},[32,3280,210],{"class":58},[32,3282,2377],{"class":102},[32,3284,517],{"class":62},[32,3286,3287,3289,3291,3293,3295],{"class":34,"line":158},[32,3288,451],{"class":58},[32,3290,2225],{"class":62},[32,3292,66],{"class":58},[32,3294,1540],{"class":72},[32,3296,692],{"class":62},[32,3298,3299,3302,3305,3307,3309,3311,3313],{"class":34,"line":179},[32,3300,3301],{"class":62},"  cache.",[32,3303,3304],{"class":102},"put",[32,3306,216],{"class":62},[32,3308,2282],{"class":264},[32,3310,85],{"class":62},[32,3312,813],{"class":72},[32,3314,312],{"class":62},[32,3316,3317,3319,3321,3323,3325,3327,3329,3331,3333,3335,3337],{"class":34,"line":184},[32,3318,451],{"class":58},[32,3320,1680],{"class":62},[32,3322,66],{"class":58},[32,3324,2243],{"class":62},[32,3326,1501],{"class":102},[32,3328,126],{"class":62},[32,3330,171],{"class":72},[32,3332,665],{"class":62},[32,3334,2282],{"class":264},[32,3336,847],{"class":62},[32,3338,3339],{"class":38},"\u002F\u002F 💥\n",[32,3341,3342,3345],{"class":34,"line":190},[32,3343,3344],{"class":102},"  print",[32,3346,3347],{"class":62},"(name);\n",[32,3349,3350],{"class":34,"line":196},[32,3351,414],{"class":62},[3180,3353,3354],{},"What happens and how to fix it?",[3356,3357,3358,3362,3387,3402,3408,3644],"details",{},[3359,3360,3361],"summary",{},"Answer",[3180,3363,3364,3367,3368,3371,3372,3374,3375,3377,3378,3380,3381,3383,3384,274],{},[29,3365,3366],{},"cache.get\u003CString>('count')"," does ",[29,3369,3370],{},"_cache['count'] as String"," — casting ",[29,3373,813],{}," (an ",[29,3376,73],{},") to ",[29,3379,171],{}," throws ",[29,3382,2905],{}," at runtime: ",[29,3385,3386],{},"type 'int' is not a subtype of type 'String'",[3180,3388,3389,3390,3392,3393,3395,3396,3398,3399,3401],{},"The ",[29,3391,2894],{}," cast blindly trusts the caller's type parameter. There's no validation — the caller asks for a ",[29,3394,171],{},", but the stored value is an ",[29,3397,73],{},". The ",[29,3400,2905],{}," surfaces deep in the cache, with no context about what was stored vs. what was requested.",[3180,3403,3404,3405,3407],{},"The fix — use ",[29,3406,2890],{}," (reified check) to validate before returning:",[23,3409,3411],{"className":25,"code":3410,"language":21,"meta":27,"style":27},"class Cache {\n  final _cache = \u003CString, Object>{};\n  void put(String key, Object value) => _cache[key] = value;\n\n  T? get\u003CT extends Object>(String key) {\n    final value = _cache[key];\n    if (value is T) return value;  \u002F\u002F reified check — only returns if actually T\n    return null;  \u002F\u002F type mismatch or missing key — no crash\n  }\n}\n\nvoid main() {\n  final cache = Cache();\n  cache.put('count', 42);\n  final count = cache.get\u003Cint>('count');    \u002F\u002F 42\n  final name = cache.get\u003CString>('count');  \u002F\u002F null (type mismatch, no crash)\n  print(name);  \u002F\u002F null — handle gracefully\n}\n",[29,3412,3413,3421,3441,3465,3469,3491,3501,3520,3531,3535,3539,3543,3551,3563,3579,3605,3630,3640],{"__ignoreMap":27},[32,3414,3415,3417,3419],{"class":34,"line":35},[32,3416,435],{"class":58},[32,3418,1540],{"class":72},[32,3420,376],{"class":62},[32,3422,3423,3425,3427,3429,3431,3433,3435,3437,3439],{"class":34,"line":42},[32,3424,451],{"class":58},[32,3426,1559],{"class":62},[32,3428,66],{"class":58},[32,3430,69],{"class":58},[32,3432,171],{"class":72},[32,3434,85],{"class":62},[32,3436,2099],{"class":72},[32,3438,76],{"class":58},[32,3440,1574],{"class":62},[32,3442,3443,3445,3447,3449,3451,3453,3455,3457,3459,3461,3463],{"class":34,"line":48},[32,3444,477],{"class":58},[32,3446,3224],{"class":102},[32,3448,216],{"class":62},[32,3450,171],{"class":72},[32,3452,1613],{"class":62},[32,3454,2099],{"class":72},[32,3456,1618],{"class":62},[32,3458,490],{"class":58},[32,3460,1623],{"class":62},[32,3462,66],{"class":58},[32,3464,1628],{"class":62},[32,3466,3467],{"class":34,"line":55},[32,3468,52],{"emptyLinePlaceholder":51},[32,3470,3471,3473,3475,3477,3479,3481,3483,3485,3487,3489],{"class":34,"line":99},[32,3472,511],{"class":72},[32,3474,1033],{"class":58},[32,3476,610],{"class":102},[32,3478,126],{"class":62},[32,3480,443],{"class":72},[32,3482,969],{"class":58},[32,3484,1549],{"class":72},[32,3486,665],{"class":62},[32,3488,171],{"class":72},[32,3490,2482],{"class":62},[32,3492,3493,3495,3497,3499],{"class":34,"line":112},[32,3494,2145],{"class":58},[32,3496,2183],{"class":62},[32,3498,66],{"class":58},[32,3500,2153],{"class":62},[32,3502,3503,3505,3507,3509,3511,3513,3515,3517],{"class":34,"line":137},[32,3504,522],{"class":58},[32,3506,2499],{"class":62},[32,3508,120],{"class":58},[32,3510,2165],{"class":72},[32,3512,2168],{"class":62},[32,3514,1078],{"class":58},[32,3516,2510],{"class":62},[32,3518,3519],{"class":38},"\u002F\u002F reified check — only returns if actually T\n",[32,3521,3522,3524,3526,3528],{"class":34,"line":158},[32,3523,543],{"class":58},[32,3525,1096],{"class":72},[32,3527,2359],{"class":62},[32,3529,3530],{"class":38},"\u002F\u002F type mismatch or missing key — no crash\n",[32,3532,3533],{"class":34,"line":179},[32,3534,408],{"class":62},[32,3536,3537],{"class":34,"line":184},[32,3538,414],{"class":62},[32,3540,3541],{"class":34,"line":190},[32,3542,52],{"emptyLinePlaceholder":51},[32,3544,3545,3547,3549],{"class":34,"line":196},[32,3546,210],{"class":58},[32,3548,2377],{"class":102},[32,3550,517],{"class":62},[32,3552,3553,3555,3557,3559,3561],{"class":34,"line":201},[32,3554,451],{"class":58},[32,3556,2225],{"class":62},[32,3558,66],{"class":58},[32,3560,1540],{"class":72},[32,3562,692],{"class":62},[32,3564,3565,3567,3569,3571,3573,3575,3577],{"class":34,"line":207},[32,3566,3301],{"class":62},[32,3568,3304],{"class":102},[32,3570,216],{"class":62},[32,3572,2282],{"class":264},[32,3574,85],{"class":62},[32,3576,813],{"class":72},[32,3578,312],{"class":62},[32,3580,3581,3583,3585,3587,3589,3591,3593,3595,3597,3599,3602],{"class":34,"line":230},[32,3582,451],{"class":58},[32,3584,2271],{"class":62},[32,3586,66],{"class":58},[32,3588,2243],{"class":62},[32,3590,1501],{"class":102},[32,3592,126],{"class":62},[32,3594,73],{"class":72},[32,3596,665],{"class":62},[32,3598,2282],{"class":264},[32,3600,3601],{"class":62},");    ",[32,3603,3604],{"class":38},"\u002F\u002F 42\n",[32,3606,3607,3609,3611,3613,3615,3617,3619,3621,3623,3625,3627],{"class":34,"line":250},[32,3608,451],{"class":58},[32,3610,1680],{"class":62},[32,3612,66],{"class":58},[32,3614,2243],{"class":62},[32,3616,1501],{"class":102},[32,3618,126],{"class":62},[32,3620,171],{"class":72},[32,3622,665],{"class":62},[32,3624,2282],{"class":264},[32,3626,847],{"class":62},[32,3628,3629],{"class":38},"\u002F\u002F null (type mismatch, no crash)\n",[32,3631,3632,3634,3637],{"class":34,"line":256},[32,3633,3344],{"class":102},[32,3635,3636],{"class":62},"(name);  ",[32,3638,3639],{"class":38},"\u002F\u002F null — handle gracefully\n",[32,3641,3642],{"class":34,"line":315},[32,3643,414],{"class":62},[3180,3645,3646,3647,3649,3650,3652,3653,3655,3656,3658,3659,3661],{},"Using ",[29,3648,2890],{}," (enabled by reified generics) validates the type at runtime. If the stored value isn't a ",[29,3651,443],{},", it returns ",[29,3654,2909],{}," instead of throwing. The caller handles the ",[29,3657,2909],{}," case — no opaque ",[29,3660,2905],{}," deep in the cache.",[3663,3664,3665],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":27,"searchDepth":42,"depth":42,"links":3667},[3668,3669,3670,3671,3674,3675,3677,3678,3679],{"id":16,"depth":42,"text":17},{"id":417,"depth":42,"text":418},{"id":883,"depth":42,"text":884},{"id":1636,"depth":42,"text":1637,"children":3672},[3673],{"id":1943,"depth":48,"text":1944},{"id":2061,"depth":42,"text":2062},{"id":2535,"depth":42,"text":3676},"Type Inference & dynamic Traps",{"id":2875,"depth":42,"text":2876},{"id":2986,"depth":42,"text":2987},{"id":3177,"depth":42,"text":3178},"Deep-dive into Dart's reified generics, bounded type parameters, covariance soundness, generic method inference, and runtime type checking patterns. Code-first engineering reference.","md",{},"\u002Fdart\u002F12-generics",{"title":5,"description":3680},"dart\u002F12-generics","6RNbt0w7jEw5-3JojcwTubdLLb7jvBQurZn6wYIQMC8",1789924647926]