[{"data":1,"prerenderedAt":1680},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-generics-wildcards-pecs":3},{"id":4,"title":5,"body":6,"description":1666,"difficulty":1667,"extension":1668,"framework":1669,"frameworkSlug":85,"meta":1670,"navigation":989,"order":120,"path":1671,"qaPath":1672,"seo":1673,"stem":1674,"subtopic":1675,"topic":1676,"topicSlug":1677,"updated":1678,"__hash__":1679},"blog\u002Fblog\u002Fjava-generics-wildcards-pecs.md","Java Generics — Wildcards, Bounded Types & the PECS Principle",{"type":7,"value":8,"toc":1654},"minimark",[9,14,49,53,80,219,254,258,292,429,470,474,509,657,689,693,726,858,881,885,905,1026,1047,1051,1065,1141,1163,1266,1280,1284,1304,1439,1451,1455,1483,1559,1585,1589,1650],[10,11,13],"h2",{"id":12},"why-wildcards-exist","Why wildcards exist",[15,16,17,18,22,23,27,28,31,32,35,36,40,41,44,45,48],"p",{},"Generics give you compile-time type safety, but they come with a surprise that trips up\nalmost everyone: ",[19,20,21],"code",{},"List\u003CInteger>"," is ",[24,25,26],"strong",{},"not"," a ",[19,29,30],{},"List\u003CNumber>",". Generics are ",[24,33,34],{},"invariant",",\nand that single fact is the reason wildcards exist. A method that wants to work over a\n",[37,38,39],"em",{},"family"," of related parameterizations — \"any list of numbers,\" \"any collection I can pour\nintegers into\" — needs a way to express that without sacrificing safety. Wildcards (",[19,42,43],{},"?",")\nand bounded type parameters (",[19,46,47],{},"\u003CT extends ...>",") are the two tools for the job, and knowing\nwhich to reach for is most of what generics interviews are really testing.",[10,50,52],{"id":51},"the-unbounded-wildcard","The unbounded wildcard",[15,54,55,56,59,60,63,64,67,68,71,72,75,76,79],{},"The simplest wildcard is ",[19,57,58],{},"\u003C?>",", read as \"a list of ",[24,61,62],{},"some unknown type",".\" You use it when\nthe method body genuinely doesn't care about the element type — it treats everything as\n",[19,65,66],{},"Object",". It is the type-safe replacement for a ",[24,69,70],{},"raw type",": a raw ",[19,73,74],{},"List"," disables type\nchecking entirely, while ",[19,77,78],{},"List\u003C?>"," keeps the compiler honest.",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-java shiki shiki-themes github-light github-dark","static void printAll(List\u003C?> list) {          \u002F\u002F any element type\n  for (Object o : list) System.out.println(o); \u002F\u002F safe: everything is an Object\n  \u002F\u002F list.add(\"x\");                            \u002F\u002F compile error — unknown element type\n}\nprintAll(List.of(1, 2, 3));   \u002F\u002F List\u003CInteger> ok\nprintAll(List.of(\"a\", \"b\"));  \u002F\u002F List\u003CString> ok\n","java","",[19,88,89,118,142,151,157,193],{"__ignoreMap":86},[90,91,94,98,101,105,109,111,114],"span",{"class":92,"line":93},"line",1,[90,95,97],{"class":96},"szBVR","static",[90,99,100],{"class":96}," void",[90,102,104],{"class":103},"sScJk"," printAll",[90,106,108],{"class":107},"sVt8B","(List",[90,110,58],{"class":96},[90,112,113],{"class":107}," list) {          ",[90,115,117],{"class":116},"sJ8bj","\u002F\u002F any element type\n",[90,119,121,124,127,130,133,136,139],{"class":92,"line":120},2,[90,122,123],{"class":96},"  for",[90,125,126],{"class":107}," (Object o ",[90,128,129],{"class":96},":",[90,131,132],{"class":107}," list) System.out.",[90,134,135],{"class":103},"println",[90,137,138],{"class":107},"(o); ",[90,140,141],{"class":116},"\u002F\u002F safe: everything is an Object\n",[90,143,145,148],{"class":92,"line":144},3,[90,146,147],{"class":116},"  \u002F\u002F list.add(\"x\");",[90,149,150],{"class":116},"                            \u002F\u002F compile error — unknown element type\n",[90,152,154],{"class":92,"line":153},4,[90,155,156],{"class":107},"}\n",[90,158,160,163,166,169,172,176,179,182,184,187,190],{"class":92,"line":159},5,[90,161,162],{"class":103},"printAll",[90,164,165],{"class":107},"(List.",[90,167,168],{"class":103},"of",[90,170,171],{"class":107},"(",[90,173,175],{"class":174},"sj4cs","1",[90,177,178],{"class":107},", ",[90,180,181],{"class":174},"2",[90,183,178],{"class":107},[90,185,186],{"class":174},"3",[90,188,189],{"class":107},"));   ",[90,191,192],{"class":116},"\u002F\u002F List\u003CInteger> ok\n",[90,194,196,198,200,202,204,208,210,213,216],{"class":92,"line":195},6,[90,197,162],{"class":103},[90,199,165],{"class":107},[90,201,168],{"class":103},[90,203,171],{"class":107},[90,205,207],{"class":206},"sZZnC","\"a\"",[90,209,178],{"class":107},[90,211,212],{"class":206},"\"b\"",[90,214,215],{"class":107},"));  ",[90,217,218],{"class":116},"\u002F\u002F List\u003CString> ok\n",[15,220,221,222,225,226,228,229,232,233,236,237,239,240,243,244,247,248,250,251,253],{},"Because the element type is hidden, an unbounded wildcard list is effectively\n",[24,223,224],{},"read-only",": you can read elements as ",[19,227,66],{},", but the only thing you can ",[37,230,231],{},"add"," is\n",[19,234,235],{},"null",". Don't confuse ",[19,238,78],{}," with ",[19,241,242],{},"List\u003CObject>"," — the latter is a single concrete\ntype that matches ",[37,245,246],{},"only"," ",[19,249,242],{},", whereas ",[19,252,78],{}," matches every parameterization.",[10,255,257],{"id":256},"upper-bounds-producers","Upper bounds: producers",[15,259,260,261,247,264,267,268,271,272,275,276,247,279,281,282,284,285,287,288,291],{},"An ",[24,262,263],{},"upper-bounded wildcard",[19,265,266],{},"\u003C? extends T>"," means \"some unknown ",[24,269,270],{},"subtype"," of ",[19,273,274],{},"T",".\" It\nshines when you want to ",[24,277,278],{},"read",[19,280,274],{}," values out of a structure. Every element is guaranteed\nto be at least a ",[19,283,274],{},", so reading as ",[19,286,274],{}," is always safe — this is the ",[24,289,290],{},"producer"," side.",[81,293,295],{"className":83,"code":294,"language":85,"meta":86,"style":86},"static double sum(List\u003C? extends Number> nums) { \u002F\u002F Number or any subtype\n  double total = 0;\n  for (Number n : nums) total += n.doubleValue(); \u002F\u002F safe: all are Numbers\n  return total;\n}\nsum(List.of(1, 2, 3));   \u002F\u002F List\u003CInteger> ok\nsum(List.of(1.5, 2.5));  \u002F\u002F List\u003CDouble> ok\n",[19,296,297,324,341,368,376,380,405],{"__ignoreMap":86},[90,298,299,301,304,307,309,312,315,318,321],{"class":92,"line":93},[90,300,97],{"class":96},[90,302,303],{"class":96}," double",[90,305,306],{"class":103}," sum",[90,308,108],{"class":107},[90,310,311],{"class":96},"\u003C?",[90,313,314],{"class":107}," extends Number",[90,316,317],{"class":96},">",[90,319,320],{"class":107}," nums) { ",[90,322,323],{"class":116},"\u002F\u002F Number or any subtype\n",[90,325,326,329,332,335,338],{"class":92,"line":120},[90,327,328],{"class":96},"  double",[90,330,331],{"class":107}," total ",[90,333,334],{"class":96},"=",[90,336,337],{"class":174}," 0",[90,339,340],{"class":107},";\n",[90,342,343,345,348,350,353,356,359,362,365],{"class":92,"line":144},[90,344,123],{"class":96},[90,346,347],{"class":107}," (Number n ",[90,349,129],{"class":96},[90,351,352],{"class":107}," nums) total ",[90,354,355],{"class":96},"+=",[90,357,358],{"class":107}," n.",[90,360,361],{"class":103},"doubleValue",[90,363,364],{"class":107},"(); ",[90,366,367],{"class":116},"\u002F\u002F safe: all are Numbers\n",[90,369,370,373],{"class":92,"line":153},[90,371,372],{"class":96},"  return",[90,374,375],{"class":107}," total;\n",[90,377,378],{"class":92,"line":159},[90,379,156],{"class":107},[90,381,382,385,387,389,391,393,395,397,399,401,403],{"class":92,"line":195},[90,383,384],{"class":103},"sum",[90,386,165],{"class":107},[90,388,168],{"class":103},[90,390,171],{"class":107},[90,392,175],{"class":174},[90,394,178],{"class":107},[90,396,181],{"class":174},[90,398,178],{"class":107},[90,400,186],{"class":174},[90,402,189],{"class":107},[90,404,192],{"class":116},[90,406,408,410,412,414,416,419,421,424,426],{"class":92,"line":407},7,[90,409,384],{"class":103},[90,411,165],{"class":107},[90,413,168],{"class":103},[90,415,171],{"class":107},[90,417,418],{"class":174},"1.5",[90,420,178],{"class":107},[90,422,423],{"class":174},"2.5",[90,425,215],{"class":107},[90,427,428],{"class":116},"\u002F\u002F List\u003CDouble> ok\n",[15,430,431,432,435,436,438,439,442,443,446,447,450,451,454,455,457,458,461,462,465,466,469],{},"What you ",[37,433,434],{},"can't"," do is ",[24,437,231],{}," to a ",[19,440,441],{},"List\u003C? extends Number>",". The compiler knows the list\nis ",[37,444,445],{},"some"," subtype of ",[19,448,449],{},"Number"," but not ",[37,452,453],{},"which"," one — it could really be a ",[19,456,21],{}," or\na ",[19,459,460],{},"List\u003CDouble>"," — so no concrete value is provably safe to insert. The list reads, it does\nnot write. The rule: ",[19,463,464],{},"extends"," is for ",[24,467,468],{},"getting",", not putting.",[10,471,473],{"id":472},"lower-bounds-consumers","Lower bounds: consumers",[15,475,476,477,247,480,483,484,271,487,489,490,247,493,495,496,499,500,502,503,505,506,291],{},"The mirror image is the ",[24,478,479],{},"lower-bounded wildcard",[19,481,482],{},"\u003C? super T>",", \"some unknown\n",[24,485,486],{},"supertype",[19,488,274],{},".\" This is what you want when you need to ",[24,491,492],{},"write",[19,494,274],{}," values ",[37,497,498],{},"into"," a\nstructure. Whatever the real type is, it is ",[19,501,274],{}," or wider, so a ",[19,504,274],{}," always fits — the\n",[24,507,508],{},"consumer",[81,510,512],{"className":83,"code":511,"language":85,"meta":86,"style":86},"static void addNumbers(List\u003C? super Integer> list) { \u002F\u002F Integer or any supertype\n  list.add(1);                                        \u002F\u002F safe — an Integer fits\n  list.add(2);\n  Object o = list.get(0);                             \u002F\u002F reads come back as Object only\n}\naddNumbers(new ArrayList\u003CInteger>()); \u002F\u002F ok\naddNumbers(new ArrayList\u003CNumber>());  \u002F\u002F ok — Integer fits a Number list\naddNumbers(new ArrayList\u003CObject>());  \u002F\u002F ok — Integer fits an Object list\n",[19,513,514,541,558,571,595,599,621,639],{"__ignoreMap":86},[90,515,516,518,520,523,525,527,530,533,535,538],{"class":92,"line":93},[90,517,97],{"class":96},[90,519,100],{"class":96},[90,521,522],{"class":103}," addNumbers",[90,524,108],{"class":107},[90,526,311],{"class":96},[90,528,529],{"class":174}," super",[90,531,532],{"class":107}," Integer",[90,534,317],{"class":96},[90,536,537],{"class":107}," list) { ",[90,539,540],{"class":116},"\u002F\u002F Integer or any supertype\n",[90,542,543,546,548,550,552,555],{"class":92,"line":120},[90,544,545],{"class":107},"  list.",[90,547,231],{"class":103},[90,549,171],{"class":107},[90,551,175],{"class":174},[90,553,554],{"class":107},");                                        ",[90,556,557],{"class":116},"\u002F\u002F safe — an Integer fits\n",[90,559,560,562,564,566,568],{"class":92,"line":144},[90,561,545],{"class":107},[90,563,231],{"class":103},[90,565,171],{"class":107},[90,567,181],{"class":174},[90,569,570],{"class":107},");\n",[90,572,573,576,578,581,584,586,589,592],{"class":92,"line":153},[90,574,575],{"class":107},"  Object o ",[90,577,334],{"class":96},[90,579,580],{"class":107}," list.",[90,582,583],{"class":103},"get",[90,585,171],{"class":107},[90,587,588],{"class":174},"0",[90,590,591],{"class":107},");                             ",[90,593,594],{"class":116},"\u002F\u002F reads come back as Object only\n",[90,596,597],{"class":92,"line":159},[90,598,156],{"class":107},[90,600,601,604,606,609,612,615,618],{"class":92,"line":195},[90,602,603],{"class":103},"addNumbers",[90,605,171],{"class":107},[90,607,608],{"class":96},"new",[90,610,611],{"class":107}," ArrayList\u003C",[90,613,614],{"class":96},"Integer",[90,616,617],{"class":107},">()); ",[90,619,620],{"class":116},"\u002F\u002F ok\n",[90,622,623,625,627,629,631,633,636],{"class":92,"line":407},[90,624,603],{"class":103},[90,626,171],{"class":107},[90,628,608],{"class":96},[90,630,611],{"class":107},[90,632,449],{"class":96},[90,634,635],{"class":107},">());  ",[90,637,638],{"class":116},"\u002F\u002F ok — Integer fits a Number list\n",[90,640,642,644,646,648,650,652,654],{"class":92,"line":641},8,[90,643,603],{"class":103},[90,645,171],{"class":107},[90,647,608],{"class":96},[90,649,611],{"class":107},[90,651,66],{"class":96},[90,653,635],{"class":107},[90,655,656],{"class":116},"\u002F\u002F ok — Integer fits an Object list\n",[15,658,659,660,663,664,666,667,670,671,22,673,675,676,678,679,682,683,685,686,688],{},"Reading from a ",[19,661,662],{},"super"," wildcard gives you back only ",[19,665,66],{},", because the widest type\ncommon to ",[37,668,669],{},"every"," supertype of ",[19,672,274],{},[19,674,66],{},". So ",[19,677,662],{}," wildcards are for ",[24,680,681],{},"putting in",";\nthe type you read out is untyped. That asymmetry — ",[19,684,464],{}," reads, ",[19,687,662],{}," writes — is the\nwhole game.",[10,690,692],{"id":691},"pecs-producer-extends-consumer-super","PECS: Producer Extends, Consumer Super",[15,694,695,696,699,700,703,704,247,707,709,710,712,713,247,716,718,719,721,722,725],{},"The mnemonic that ties it together is ",[24,697,698],{},"PECS",": ",[24,701,702],{},"Producer Extends, Consumer Super."," When a\nparameter ",[37,705,706],{},"produces",[19,708,274],{},"s for you to read, use ",[19,711,266],{},"; when it ",[37,714,715],{},"consumes",[19,717,274],{},"s you\nsupply, use ",[19,720,482],{},". The canonical demonstration is a ",[19,723,724],{},"copy"," method, where the source\nproduces and the destination consumes.",[81,727,729],{"className":83,"code":728,"language":85,"meta":86,"style":86},"\u002F\u002F src PRODUCES elements -> extends; dest CONSUMES elements -> super\nstatic \u003CT> void copy(List\u003C? extends T> src, List\u003C? super T> dest) {\n  for (T t : src) dest.add(t);   \u002F\u002F read from src, write to dest\n}\nList\u003CInteger> ints = List.of(1, 2, 3);\nList\u003CNumber>  dst  = new ArrayList\u003C>();\ncopy(ints, dst);                 \u002F\u002F Integer producer -> Number consumer\n",[19,730,731,736,776,796,800,831,848],{"__ignoreMap":86},[90,732,733],{"class":92,"line":93},[90,734,735],{"class":116},"\u002F\u002F src PRODUCES elements -> extends; dest CONSUMES elements -> super\n",[90,737,738,740,743,745,747,749,752,754,756,759,761,764,766,768,771,773],{"class":92,"line":120},[90,739,97],{"class":96},[90,741,742],{"class":96}," \u003C",[90,744,274],{"class":107},[90,746,317],{"class":96},[90,748,100],{"class":96},[90,750,751],{"class":103}," copy",[90,753,108],{"class":107},[90,755,311],{"class":96},[90,757,758],{"class":107}," extends T",[90,760,317],{"class":96},[90,762,763],{"class":107}," src, List",[90,765,311],{"class":96},[90,767,529],{"class":174},[90,769,770],{"class":107}," T",[90,772,317],{"class":96},[90,774,775],{"class":107}," dest) {\n",[90,777,778,780,783,785,788,790,793],{"class":92,"line":144},[90,779,123],{"class":96},[90,781,782],{"class":107}," (T t ",[90,784,129],{"class":96},[90,786,787],{"class":107}," src) dest.",[90,789,231],{"class":103},[90,791,792],{"class":107},"(t);   ",[90,794,795],{"class":116},"\u002F\u002F read from src, write to dest\n",[90,797,798],{"class":92,"line":153},[90,799,156],{"class":107},[90,801,802,805,807,810,812,815,817,819,821,823,825,827,829],{"class":92,"line":159},[90,803,804],{"class":107},"List\u003C",[90,806,614],{"class":96},[90,808,809],{"class":107},"> ints ",[90,811,334],{"class":96},[90,813,814],{"class":107}," List.",[90,816,168],{"class":103},[90,818,171],{"class":107},[90,820,175],{"class":174},[90,822,178],{"class":107},[90,824,181],{"class":174},[90,826,178],{"class":107},[90,828,186],{"class":174},[90,830,570],{"class":107},[90,832,833,835,837,840,842,845],{"class":92,"line":195},[90,834,804],{"class":107},[90,836,449],{"class":96},[90,838,839],{"class":107},">  dst  ",[90,841,334],{"class":96},[90,843,844],{"class":96}," new",[90,846,847],{"class":107}," ArrayList\u003C>();\n",[90,849,850,852,855],{"class":92,"line":407},[90,851,724],{"class":103},[90,853,854],{"class":107},"(ints, dst);                 ",[90,856,857],{"class":116},"\u002F\u002F Integer producer -> Number consumer\n",[15,859,860,861,864,865,868,869,872,873,876,877,880],{},"This is exactly how ",[19,862,863],{},"Collections.copy"," is declared, and the payoff is ",[24,866,867],{},"API flexibility",":\ncallers can pass a wider range of list types on each side than a rigid ",[19,870,871],{},"List\u003CT>"," would\nallow. You see the same idea throughout the standard library — ",[19,874,875],{},"Collection.addAll"," takes\n",[19,878,879],{},"Collection\u003C? extends E>"," because the argument is a producer it only reads from.",[10,882,884],{"id":883},"bounded-type-parameters-vs-wildcards","Bounded type parameters vs wildcards",[15,886,887,888,891,894,895,897,898,900,901,904],{},"Wildcards aren't the only way to constrain a type. A ",[24,889,890],{},"bounded type parameter",[19,892,893],{},"\u003CT extends Number>"," also restricts ",[19,896,274],{}," to ",[19,899,449],{}," or below — so when do you pick which?\nThe deciding question is whether you need to ",[24,902,903],{},"name"," the type. A type parameter has a name\nyou can reuse; a wildcard does not.",[81,906,908],{"className":83,"code":907,"language":85,"meta":86,"style":86},"\u002F\u002F type parameter: the SAME T links the input and the return type\nstatic \u003CT extends Number> T firstOf(List\u003CT> list) { return list.get(0); }\nInteger i = firstOf(List.of(1, 2)); \u002F\u002F returns Integer, not just Number\n\n\u002F\u002F wildcard: the type is used once and never named\nstatic double sumOf(List\u003C? extends Number> list) { \u002F* ... *\u002F return 0; }\n",[19,909,910,915,957,985,991,996],{"__ignoreMap":86},[90,911,912],{"class":92,"line":93},[90,913,914],{"class":116},"\u002F\u002F type parameter: the SAME T links the input and the return type\n",[90,916,917,919,921,924,926,929,932,934,937,939,941,943,946,948,950,952,954],{"class":92,"line":120},[90,918,97],{"class":96},[90,920,742],{"class":96},[90,922,923],{"class":107},"T extends Number",[90,925,317],{"class":96},[90,927,928],{"class":107}," T ",[90,930,931],{"class":103},"firstOf",[90,933,108],{"class":107},[90,935,936],{"class":96},"\u003C",[90,938,274],{"class":107},[90,940,317],{"class":96},[90,942,537],{"class":107},[90,944,945],{"class":96},"return",[90,947,580],{"class":107},[90,949,583],{"class":103},[90,951,171],{"class":107},[90,953,588],{"class":174},[90,955,956],{"class":107},"); }\n",[90,958,959,962,964,967,969,971,973,975,977,979,982],{"class":92,"line":144},[90,960,961],{"class":107},"Integer i ",[90,963,334],{"class":96},[90,965,966],{"class":103}," firstOf",[90,968,165],{"class":107},[90,970,168],{"class":103},[90,972,171],{"class":107},[90,974,175],{"class":174},[90,976,178],{"class":107},[90,978,181],{"class":174},[90,980,981],{"class":107},")); ",[90,983,984],{"class":116},"\u002F\u002F returns Integer, not just Number\n",[90,986,987],{"class":92,"line":153},[90,988,990],{"emptyLinePlaceholder":989},true,"\n",[90,992,993],{"class":92,"line":159},[90,994,995],{"class":116},"\u002F\u002F wildcard: the type is used once and never named\n",[90,997,998,1000,1002,1005,1007,1009,1011,1013,1015,1018,1021,1023],{"class":92,"line":195},[90,999,97],{"class":96},[90,1001,303],{"class":96},[90,1003,1004],{"class":103}," sumOf",[90,1006,108],{"class":107},[90,1008,311],{"class":96},[90,1010,314],{"class":107},[90,1012,317],{"class":96},[90,1014,537],{"class":107},[90,1016,1017],{"class":116},"\u002F* ... *\u002F",[90,1019,1020],{"class":96}," return",[90,1022,337],{"class":174},[90,1024,1025],{"class":107},"; }\n",[15,1027,1028,1029,1032,1033,1036,1037,1039,1040,1042,1043,1046],{},"Use a ",[24,1030,1031],{},"type parameter"," when the type appears more than once — relating two arguments, or\nflowing back through the return type. Use a ",[24,1034,1035],{},"wildcard"," when the type shows up exactly once\nand you never need to refer to it. A wildcard has no name, so the most a method can return\nfrom ",[19,1038,441],{}," is a plain ",[19,1041,449],{},"; if the output must match the caller's\nexact element type, you ",[37,1044,1045],{},"need"," the named parameter.",[10,1048,1050],{"id":1049},"multiple-and-recursive-bounds","Multiple and recursive bounds",[15,1052,1053,1054,1057,1058,1061,1062,1064],{},"A type parameter can demand ",[24,1055,1056],{},"several bounds"," at once with ",[19,1059,1060],{},"&",", meaning ",[19,1063,274],{}," must satisfy\nall of them. At most one bound may be a class, and if present it must come first; the rest\nmust be interfaces. Wildcards can carry only a single bound, so this is type-parameter-only\nterritory.",[81,1066,1068],{"className":83,"code":1067,"language":85,"meta":86,"style":86},"\u002F\u002F T must be BOTH Comparable AND Serializable\nstatic \u003CT extends Comparable\u003CT> & Serializable> T maxOf(T a, T b) {\n  return a.compareTo(b) >= 0 ? a : b;   \u002F\u002F Comparable methods available on T\n}\n",[19,1069,1070,1075,1106,1137],{"__ignoreMap":86},[90,1071,1072],{"class":92,"line":93},[90,1073,1074],{"class":116},"\u002F\u002F T must be BOTH Comparable AND Serializable\n",[90,1076,1077,1079,1081,1084,1086,1088,1090,1093,1096,1098,1100,1103],{"class":92,"line":120},[90,1078,97],{"class":96},[90,1080,742],{"class":96},[90,1082,1083],{"class":107},"T extends Comparable",[90,1085,936],{"class":96},[90,1087,274],{"class":107},[90,1089,317],{"class":96},[90,1091,1092],{"class":96}," &",[90,1094,1095],{"class":107}," Serializable",[90,1097,317],{"class":96},[90,1099,928],{"class":107},[90,1101,1102],{"class":103},"maxOf",[90,1104,1105],{"class":107},"(T a, T b) {\n",[90,1107,1108,1110,1113,1116,1119,1122,1124,1127,1129,1131,1134],{"class":92,"line":144},[90,1109,372],{"class":96},[90,1111,1112],{"class":107}," a.",[90,1114,1115],{"class":103},"compareTo",[90,1117,1118],{"class":107},"(b) ",[90,1120,1121],{"class":96},">=",[90,1123,337],{"class":174},[90,1125,1126],{"class":96}," ?",[90,1128,27],{"class":107},[90,1130,129],{"class":96},[90,1132,1133],{"class":107}," b;   ",[90,1135,1136],{"class":116},"\u002F\u002F Comparable methods available on T\n",[90,1138,1139],{"class":92,"line":153},[90,1140,156],{"class":107},[15,1142,1143,1144,247,1147,1150,1151,1153,1154,1157,1158,897,1160,1162],{},"A special case you'll see constantly is the ",[24,1145,1146],{},"recursive bound",[19,1148,1149],{},"\u003CT extends Comparable\u003CT>>","\n— \"",[19,1152,274],{}," must be comparable ",[24,1155,1156],{},"to itself",".\" It captures the notion \"this type knows how to\norder its own instances,\" which is precisely what sorting and min\u002Fmax require; without the\nrecursion you couldn't safely pass a ",[19,1159,274],{},[19,1161,1115],{},".",[81,1164,1166],{"className":83,"code":1165,"language":85,"meta":86,"style":86},"static \u003CT extends Comparable\u003CT>> T max(List\u003CT> list) {\n  T best = list.get(0);\n  for (T t : list)\n    if (t.compareTo(best) > 0) best = t; \u002F\u002F t.compareTo(T) is type-safe\n  return best;\n}\n",[19,1167,1168,1199,1216,1227,1255,1262],{"__ignoreMap":86},[90,1169,1170,1172,1174,1176,1178,1180,1183,1185,1188,1190,1192,1194,1196],{"class":92,"line":93},[90,1171,97],{"class":96},[90,1173,742],{"class":96},[90,1175,1083],{"class":107},[90,1177,936],{"class":96},[90,1179,274],{"class":107},[90,1181,1182],{"class":96},">>",[90,1184,928],{"class":107},[90,1186,1187],{"class":103},"max",[90,1189,108],{"class":107},[90,1191,936],{"class":96},[90,1193,274],{"class":107},[90,1195,317],{"class":96},[90,1197,1198],{"class":107}," list) {\n",[90,1200,1201,1204,1206,1208,1210,1212,1214],{"class":92,"line":120},[90,1202,1203],{"class":107},"  T best ",[90,1205,334],{"class":96},[90,1207,580],{"class":107},[90,1209,583],{"class":103},[90,1211,171],{"class":107},[90,1213,588],{"class":174},[90,1215,570],{"class":107},[90,1217,1218,1220,1222,1224],{"class":92,"line":144},[90,1219,123],{"class":96},[90,1221,782],{"class":107},[90,1223,129],{"class":96},[90,1225,1226],{"class":107}," list)\n",[90,1228,1229,1232,1235,1237,1240,1242,1244,1247,1249,1252],{"class":92,"line":153},[90,1230,1231],{"class":96},"    if",[90,1233,1234],{"class":107}," (t.",[90,1236,1115],{"class":103},[90,1238,1239],{"class":107},"(best) ",[90,1241,317],{"class":96},[90,1243,337],{"class":174},[90,1245,1246],{"class":107},") best ",[90,1248,334],{"class":96},[90,1250,1251],{"class":107}," t; ",[90,1253,1254],{"class":116},"\u002F\u002F t.compareTo(T) is type-safe\n",[90,1256,1257,1259],{"class":92,"line":159},[90,1258,372],{"class":96},[90,1260,1261],{"class":107}," best;\n",[90,1263,1264],{"class":92,"line":195},[90,1265,156],{"class":107},[15,1267,1268,1269,1272,1273,1276,1277,1279],{},"The real ",[19,1270,1271],{},"Collections.max"," loosens this to ",[19,1274,1275],{},"\u003CT extends Comparable\u003C? super T>>"," — PECS again,\nso a subtype can reuse an ancestor's ",[19,1278,1115],{}," instead of redeclaring its own.",[10,1281,1283],{"id":1282},"wildcard-capture","Wildcard capture",[15,1285,1286,1287,1289,1290,1292,1293,1296,1297,1299,1300,1303],{},"Sometimes you have a ",[19,1288,58],{}," signature but the method body needs to ",[37,1291,903],{}," that unknown type.\nThe compiler assigns it a temporary name (you'll see ",[19,1294,1295],{},"CAP#1"," in errors), but it won't let\nyou, say, take an element out of a ",[19,1298,78],{}," and put it back in — it can't prove the two\nends match. The fix is the ",[24,1301,1302],{},"capture-helper pattern",": a private generic method that\ncaptures the wildcard into a real type variable.",[81,1305,1307],{"className":83,"code":1306,"language":85,"meta":86,"style":86},"\u002F\u002F public method keeps the clean \u003C?> signature\nstatic void swap(List\u003C?> list, int i, int j) {\n  swapHelper(list, i, j);                 \u002F\u002F delegate to capture the wildcard\n}\nprivate static \u003CT> void swapHelper(List\u003CT> list, int i, int j) {\n  T tmp = list.get(i);                    \u002F\u002F now there is a concrete T\n  list.set(i, list.get(j));\n  list.set(j, tmp);\n}\n",[19,1308,1309,1314,1341,1352,1356,1393,1410,1425,1434],{"__ignoreMap":86},[90,1310,1311],{"class":92,"line":93},[90,1312,1313],{"class":116},"\u002F\u002F public method keeps the clean \u003C?> signature\n",[90,1315,1316,1318,1320,1323,1325,1327,1330,1333,1336,1338],{"class":92,"line":120},[90,1317,97],{"class":96},[90,1319,100],{"class":96},[90,1321,1322],{"class":103}," swap",[90,1324,108],{"class":107},[90,1326,58],{"class":96},[90,1328,1329],{"class":107}," list, ",[90,1331,1332],{"class":96},"int",[90,1334,1335],{"class":107}," i, ",[90,1337,1332],{"class":96},[90,1339,1340],{"class":107}," j) {\n",[90,1342,1343,1346,1349],{"class":92,"line":144},[90,1344,1345],{"class":103},"  swapHelper",[90,1347,1348],{"class":107},"(list, i, j);                 ",[90,1350,1351],{"class":116},"\u002F\u002F delegate to capture the wildcard\n",[90,1353,1354],{"class":92,"line":153},[90,1355,156],{"class":107},[90,1357,1358,1361,1364,1366,1368,1370,1372,1375,1377,1379,1381,1383,1385,1387,1389,1391],{"class":92,"line":159},[90,1359,1360],{"class":96},"private",[90,1362,1363],{"class":96}," static",[90,1365,742],{"class":96},[90,1367,274],{"class":107},[90,1369,317],{"class":96},[90,1371,100],{"class":96},[90,1373,1374],{"class":103}," swapHelper",[90,1376,108],{"class":107},[90,1378,936],{"class":96},[90,1380,274],{"class":107},[90,1382,317],{"class":96},[90,1384,1329],{"class":107},[90,1386,1332],{"class":96},[90,1388,1335],{"class":107},[90,1390,1332],{"class":96},[90,1392,1340],{"class":107},[90,1394,1395,1398,1400,1402,1404,1407],{"class":92,"line":195},[90,1396,1397],{"class":107},"  T tmp ",[90,1399,334],{"class":96},[90,1401,580],{"class":107},[90,1403,583],{"class":103},[90,1405,1406],{"class":107},"(i);                    ",[90,1408,1409],{"class":116},"\u002F\u002F now there is a concrete T\n",[90,1411,1412,1414,1417,1420,1422],{"class":92,"line":407},[90,1413,545],{"class":107},[90,1415,1416],{"class":103},"set",[90,1418,1419],{"class":107},"(i, list.",[90,1421,583],{"class":103},[90,1423,1424],{"class":107},"(j));\n",[90,1426,1427,1429,1431],{"class":92,"line":641},[90,1428,545],{"class":107},[90,1430,1416],{"class":103},[90,1432,1433],{"class":107},"(j, tmp);\n",[90,1435,1437],{"class":92,"line":1436},9,[90,1438,156],{"class":107},[15,1440,1441,1442,247,1444,1447,1448,1450],{},"The helper's ",[19,1443,274],{},[37,1445,1446],{},"captures"," the wildcard, so inside it the get-and-set both type-check while\ncallers still see the tidy ",[19,1449,78],{}," API.",[10,1452,1454],{"id":1453},"array-covariance-vs-generic-invariance","Array covariance vs generic invariance",[15,1456,1457,1458,1461,1462,247,1465,247,1468,1471,1472,1461,1475,22,1478,27,1480,1482],{},"Why is any of this necessary? Because Java made opposite choices for arrays and generics.\n",[24,1459,1460],{},"Arrays are covariant"," — ",[19,1463,1464],{},"String[]",[37,1466,1467],{},"is an",[19,1469,1470],{},"Object[]"," — which feels convenient but defers\nsafety to runtime. ",[24,1473,1474],{},"Generics are invariant",[19,1476,1477],{},"List\u003CString>",[37,1479,26],{},[19,1481,242],{}," —\ncatching the mistake at compile time instead.",[81,1484,1486],{"className":83,"code":1485,"language":85,"meta":86,"style":86},"Object[] arr = new String[3];\narr[0] = 42;                                  \u002F\u002F compiles, throws ArrayStoreException at RUNTIME\n\nList\u003CObject> list = new ArrayList\u003CString>();  \u002F\u002F compile error — caught early\n",[19,1487,1488,1510,1531,1535],{"__ignoreMap":86},[90,1489,1490,1492,1495,1497,1499,1502,1505,1507],{"class":92,"line":93},[90,1491,66],{"class":96},[90,1493,1494],{"class":107},"[] arr ",[90,1496,334],{"class":96},[90,1498,844],{"class":96},[90,1500,1501],{"class":96}," String",[90,1503,1504],{"class":107},"[",[90,1506,186],{"class":174},[90,1508,1509],{"class":107},"];\n",[90,1511,1512,1515,1517,1520,1522,1525,1528],{"class":92,"line":120},[90,1513,1514],{"class":107},"arr[",[90,1516,588],{"class":174},[90,1518,1519],{"class":107},"] ",[90,1521,334],{"class":96},[90,1523,1524],{"class":174}," 42",[90,1526,1527],{"class":107},";                                  ",[90,1529,1530],{"class":116},"\u002F\u002F compiles, throws ArrayStoreException at RUNTIME\n",[90,1532,1533],{"class":92,"line":144},[90,1534,990],{"emptyLinePlaceholder":989},[90,1536,1537,1539,1541,1544,1546,1548,1550,1553,1556],{"class":92,"line":153},[90,1538,804],{"class":107},[90,1540,66],{"class":96},[90,1542,1543],{"class":107},"> list ",[90,1545,334],{"class":96},[90,1547,844],{"class":96},[90,1549,611],{"class":107},[90,1551,1552],{"class":96},"String",[90,1554,1555],{"class":107},">();  ",[90,1557,1558],{"class":116},"\u002F\u002F compile error — caught early\n",[15,1560,1561,1562,1565,1566,1569,1570,699,1573,1575,1576,1578,1579,1581,1582,1584],{},"Generics chose invariance because ",[24,1563,1564],{},"erasure"," removes type info at runtime — there is no\n",[19,1567,1568],{},"ArrayStore"," check to fall back on, so the unsafe assignment is forbidden up front.\nWildcards then hand the lost flexibility back, ",[37,1571,1572],{},"safely",[19,1574,441],{}," accepts a\n",[19,1577,21],{}," for reading without ever letting you insert the wrong element. They are the\nopt-in bridge between strict invariance and the covariance (",[19,1580,464],{},") or contravariance\n(",[19,1583,662],{},") you actually want — applied exactly where it's provably sound.",[10,1586,1588],{"id":1587},"recap","Recap",[15,1590,1591,1592,1594,1595,1598,1599,1601,1602,1604,1605,1608,1609,1611,1612,1615,1616,1618,1619,1621,1622,1625,1626,1629,1630,1633,1634,1636,1637,1461,1640,1642,1643,1645,1646,1649],{},"Generics are ",[24,1593,34],{}," by design, and ",[24,1596,1597],{},"wildcards"," restore the flexibility that\ninvariance costs. Use ",[19,1600,58],{}," when the element type is irrelevant; use ",[19,1603,266],{}," for\n",[24,1606,1607],{},"producers"," you read from and ",[19,1610,482],{}," for ",[24,1613,1614],{},"consumers"," you write to — the\n",[24,1617,698],{}," rule, \"Producer Extends, Consumer Super.\" Reach for a ",[24,1620,890],{},"\ninstead of a wildcard whenever you must name the type to reuse it or return it, and lean on\n",[24,1623,1624],{},"multiple bounds"," (",[19,1627,1628],{},"\u003CT extends A & B>",") and ",[24,1631,1632],{},"recursive bounds","\n(",[19,1635,1149],{},") when one constraint isn't enough. Remember the ",[24,1638,1639],{},"get-and-put\nprinciple",[19,1641,464],{}," to get, ",[19,1644,662],{}," to put, an exact type to do both — keep the\n",[24,1647,1648],{},"capture-helper"," pattern in your back pocket, and you'll read the standard library's\ngnarly signatures, and write your own, with confidence.",[1651,1652,1653],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":86,"searchDepth":120,"depth":120,"links":1655},[1656,1657,1658,1659,1660,1661,1662,1663,1664,1665],{"id":12,"depth":120,"text":13},{"id":51,"depth":120,"text":52},{"id":256,"depth":120,"text":257},{"id":472,"depth":120,"text":473},{"id":691,"depth":120,"text":692},{"id":883,"depth":120,"text":884},{"id":1049,"depth":120,"text":1050},{"id":1282,"depth":120,"text":1283},{"id":1453,"depth":120,"text":1454},{"id":1587,"depth":120,"text":1588},"A guide to Java wildcards and bounded types — unbounded, upper- and lower-bounded wildcards, the PECS principle, bounded type parameters, multiple and recursive bounds, wildcard capture, and why generics are invariant.","hard","md","Java",{},"\u002Fblog\u002Fjava-generics-wildcards-pecs","\u002Fjava\u002Fgenerics\u002Fwildcards-bounds",{"title":5,"description":1666},"blog\u002Fjava-generics-wildcards-pecs","Wildcards & Bounded Types","Generics","generics","2026-06-20","7Y99hN7N9dQiUdPbjpTtK2FOuobvgg7W_c28jNlyQRw",1782244089522]