[{"data":1,"prerenderedAt":1498},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-generics-basics-type-safety":3},{"id":4,"title":5,"body":6,"description":1485,"difficulty":1486,"extension":1487,"framework":1488,"frameworkSlug":57,"meta":1489,"navigation":253,"order":65,"path":1490,"qaPath":1491,"seo":1492,"stem":1493,"subtopic":1494,"topic":38,"topicSlug":1495,"updated":1496,"__hash__":1497},"blog\u002Fblog\u002Fjava-generics-basics-type-safety.md","Java Generics — Type Safety, Generic Classes, Methods & the Diamond Operator",{"type":7,"value":8,"toc":1473},"minimark",[9,14,52,151,154,158,165,308,375,379,386,427,483,487,503,615,629,633,656,727,758,762,788,861,879,883,903,978,1005,1009,1020,1158,1171,1175,1182,1254,1268,1394,1400,1404,1469],[10,11,13],"h2",{"id":12},"why-generics-exist","Why generics exist",[15,16,17,18,22,23,26,27,31,32,35,36,39,40,43,44,47,48,51],"p",{},"Before Java 5, a collection held plain ",[19,20,21],"code",{},"Object",". You could add anything to a ",[19,24,25],{},"List",", but\nevery time you read a value back you had to ",[28,29,30],"strong",{},"cast"," it, and if you guessed wrong the\nprogram blew up at runtime with a ",[19,33,34],{},"ClassCastException",". ",[28,37,38],{},"Generics"," fix this by letting\nyou parameterize a class, interface, or method by ",[28,41,42],{},"type",", so the same code works for\nmany types while the compiler still enforces correctness. The two payoffs are\n",[28,45,46],{},"compile-time type checking"," — a wrong type is a build error, not a 3 a.m. crash — and\n",[28,49,50],{},"no manual casts"," when you read values out.",[53,54,59],"pre",{"className":55,"code":56,"language":57,"meta":58,"style":58},"language-java shiki shiki-themes github-light github-dark","List\u003CString> names = new ArrayList\u003C>();\nnames.add(\"Ada\");\nnames.add(42);            \u002F\u002F compile error — caught at build time\nString n = names.get(0);  \u002F\u002F no cast needed; the compiler knows it's a String\n","java","",[19,60,61,86,106,126],{"__ignoreMap":58},[62,63,66,70,74,77,80,83],"span",{"class":64,"line":65},"line",1,[62,67,69],{"class":68},"sVt8B","List\u003C",[62,71,73],{"class":72},"szBVR","String",[62,75,76],{"class":68},"> names ",[62,78,79],{"class":72},"=",[62,81,82],{"class":72}," new",[62,84,85],{"class":68}," ArrayList\u003C>();\n",[62,87,89,92,96,99,103],{"class":64,"line":88},2,[62,90,91],{"class":68},"names.",[62,93,95],{"class":94},"sScJk","add",[62,97,98],{"class":68},"(",[62,100,102],{"class":101},"sZZnC","\"Ada\"",[62,104,105],{"class":68},");\n",[62,107,109,111,113,115,119,122],{"class":64,"line":108},3,[62,110,91],{"class":68},[62,112,95],{"class":94},[62,114,98],{"class":68},[62,116,118],{"class":117},"sj4cs","42",[62,120,121],{"class":68},");            ",[62,123,125],{"class":124},"sJ8bj","\u002F\u002F compile error — caught at build time\n",[62,127,129,132,134,137,140,142,145,148],{"class":64,"line":128},4,[62,130,131],{"class":68},"String n ",[62,133,79],{"class":72},[62,135,136],{"class":68}," names.",[62,138,139],{"class":94},"get",[62,141,98],{"class":68},[62,143,144],{"class":117},"0",[62,146,147],{"class":68},");  ",[62,149,150],{"class":124},"\u002F\u002F no cast needed; the compiler knows it's a String\n",[15,152,153],{},"That single shift — moving type failures from runtime to compile time — is the whole\nreason generics matter, and it underpins everything else in this guide.",[10,155,157],{"id":156},"generic-classes","Generic classes",[15,159,160,161,164],{},"You declare a generic class by adding a ",[28,162,163],{},"type parameter"," in angle brackets after the\nclass name. That parameter then stands in for a real type everywhere inside the class, and\nthe caller supplies the concrete type when they create an instance.",[53,166,168],{"className":55,"code":167,"language":57,"meta":58,"style":58},"class Box\u003CT> {              \u002F\u002F T is the type parameter\n  private T value;\n  public void set(T v) { value = v; }\n  public T get() { return value; }\n}\n\nBox\u003CString> b = new Box\u003C>();\nb.set(\"hi\");\nString s = b.get();        \u002F\u002F returns T = String, no cast\n",[19,169,170,190,198,224,242,248,255,273,289],{"__ignoreMap":58},[62,171,172,175,178,181,184,187],{"class":64,"line":65},[62,173,174],{"class":72},"class",[62,176,177],{"class":94}," Box",[62,179,180],{"class":68},"\u003C",[62,182,183],{"class":72},"T",[62,185,186],{"class":68},"> {              ",[62,188,189],{"class":124},"\u002F\u002F T is the type parameter\n",[62,191,192,195],{"class":64,"line":88},[62,193,194],{"class":72},"  private",[62,196,197],{"class":68}," T value;\n",[62,199,200,203,206,209,212,216,219,221],{"class":64,"line":108},[62,201,202],{"class":72},"  public",[62,204,205],{"class":72}," void",[62,207,208],{"class":94}," set",[62,210,211],{"class":68},"(T ",[62,213,215],{"class":214},"s4XuR","v",[62,217,218],{"class":68},") { value ",[62,220,79],{"class":72},[62,222,223],{"class":68}," v; }\n",[62,225,226,228,231,233,236,239],{"class":64,"line":128},[62,227,202],{"class":72},[62,229,230],{"class":68}," T ",[62,232,139],{"class":94},[62,234,235],{"class":68},"() { ",[62,237,238],{"class":72},"return",[62,240,241],{"class":68}," value; }\n",[62,243,245],{"class":64,"line":244},5,[62,246,247],{"class":68},"}\n",[62,249,251],{"class":64,"line":250},6,[62,252,254],{"emptyLinePlaceholder":253},true,"\n",[62,256,258,261,263,266,268,270],{"class":64,"line":257},7,[62,259,260],{"class":68},"Box\u003C",[62,262,73],{"class":72},[62,264,265],{"class":68},"> b ",[62,267,79],{"class":72},[62,269,82],{"class":72},[62,271,272],{"class":68}," Box\u003C>();\n",[62,274,276,279,282,284,287],{"class":64,"line":275},8,[62,277,278],{"class":68},"b.",[62,280,281],{"class":94},"set",[62,283,98],{"class":68},[62,285,286],{"class":101},"\"hi\"",[62,288,105],{"class":68},[62,290,292,295,297,300,302,305],{"class":64,"line":291},9,[62,293,294],{"class":68},"String s ",[62,296,79],{"class":72},[62,298,299],{"class":68}," b.",[62,301,139],{"class":94},[62,303,304],{"class":68},"();        ",[62,306,307],{"class":124},"\u002F\u002F returns T = String, no cast\n",[15,309,310,311,313,314,316,317,320,321,324,325,327,328,331,332,335,336,338,339,342,343,346,347,350,351,354,355,358,359,362,363,366,367,370,371,374],{},"For that instance the type argument ",[19,312,73],{}," replaces ",[19,315,183],{}," everywhere, so the compiler\nrejects ",[19,318,319],{},"b.set(42)"," and lets ",[19,322,323],{},"get()"," hand you a ",[19,326,73],{}," directly. ",[28,329,330],{},"Generic interfaces","\nwork identically — ",[19,333,334],{},"interface Repository\u003CT>"," declares ",[19,337,183],{},", and an implementor either\n",[28,340,341],{},"fixes"," the type (",[19,344,345],{},"class UserRepo implements Repository\u003CUser>",") or ",[28,348,349],{},"stays generic","\n(",[19,352,353],{},"class MemRepo\u003CT> implements Repository\u003CT>","). Core JDK contracts like ",[19,356,357],{},"Comparable\u003CT>",",\n",[19,360,361],{},"Iterable\u003CE>",", and ",[19,364,365],{},"Comparator\u003CT>"," are exactly this pattern, which is why\n",[19,368,369],{},"class Money implements Comparable\u003CMoney>"," gives you a type-safe ",[19,372,373],{},"compareTo(Money)"," with\nno downcast.",[10,376,378],{"id":377},"naming-conventions-for-type-parameters","Naming conventions for type parameters",[15,380,381,382,385],{},"Type parameters are ",[28,383,384],{},"single uppercase letters"," by convention, each hinting at a role.\nThe compiler accepts any valid identifier, but following the convention makes a generic\nsignature readable at a glance.",[53,387,389],{"className":55,"code":388,"language":57,"meta":58,"style":58},"interface Map\u003CK, V> { V get(K key); }   \u002F\u002F K = key, V = value\n",[19,390,391],{"__ignoreMap":58},[62,392,393,396,399,401,404,407,410,413,415,418,421,424],{"class":64,"line":65},[62,394,395],{"class":72},"interface",[62,397,398],{"class":94}," Map",[62,400,180],{"class":68},[62,402,403],{"class":72},"K",[62,405,406],{"class":68},", ",[62,408,409],{"class":72},"V",[62,411,412],{"class":68},"> { V ",[62,414,139],{"class":94},[62,416,417],{"class":68},"(K ",[62,419,420],{"class":214},"key",[62,422,423],{"class":68},"); }   ",[62,425,426],{"class":124},"\u002F\u002F K = key, V = value\n",[15,428,429,430,434,435,440,441,445,446,450,451,456,457,462,463,468,469,474,475,478,479,482],{},"The common letters: ",[28,431,432],{},[19,433,183],{}," for a generic type, ",[28,436,437],{},[19,438,439],{},"E"," for an element (used throughout\nthe collections framework), ",[28,442,443],{},[19,444,403],{}," and ",[28,447,448],{},[19,449,409],{}," for a map's key and value, ",[28,452,453],{},[19,454,455],{},"N"," for a\nnumber, ",[28,458,459],{},[19,460,461],{},"R"," for a return type, and ",[28,464,465],{},[19,466,467],{},"S","\u002F",[28,470,471],{},[19,472,473],{},"U"," when a second or third type is\nneeded. When you see ",[19,476,477],{},"\u003CE>"," on a collection or ",[19,480,481],{},"\u003CK, V>"," on a map, you already know what the\nauthor meant — that shared vocabulary is the entire point.",[10,484,486],{"id":485},"generic-methods","Generic methods",[15,488,489,490,493,494,497,498,502],{},"A class isn't the only thing that can be generic. A ",[28,491,492],{},"generic method"," declares its\n",[28,495,496],{},"own"," type parameter, written ",[499,500,501],"em",{},"before"," the return type, and that parameter is scoped to\nthe single method. It can live in any class, generic or not.",[53,504,506],{"className":55,"code":505,"language":57,"meta":58,"style":58},"\u002F\u002F \u003CT> introduces the type parameter for this method only\nstatic \u003CT> T firstOrNull(List\u003CT> list) {\n  return list.isEmpty() ? null : list.get(0);\n}\n\nString s = firstOrNull(List.of(\"a\", \"b\")); \u002F\u002F T inferred as String\n",[19,507,508,513,543,576,580,584],{"__ignoreMap":58},[62,509,510],{"class":64,"line":65},[62,511,512],{"class":124},"\u002F\u002F \u003CT> introduces the type parameter for this method only\n",[62,514,515,518,521,523,526,528,531,534,536,538,540],{"class":64,"line":88},[62,516,517],{"class":72},"static",[62,519,520],{"class":72}," \u003C",[62,522,183],{"class":68},[62,524,525],{"class":72},">",[62,527,230],{"class":68},[62,529,530],{"class":94},"firstOrNull",[62,532,533],{"class":68},"(List",[62,535,180],{"class":72},[62,537,183],{"class":68},[62,539,525],{"class":72},[62,541,542],{"class":68}," list) {\n",[62,544,545,548,551,554,557,560,563,566,568,570,572,574],{"class":64,"line":108},[62,546,547],{"class":72},"  return",[62,549,550],{"class":68}," list.",[62,552,553],{"class":94},"isEmpty",[62,555,556],{"class":68},"() ",[62,558,559],{"class":72},"?",[62,561,562],{"class":117}," null",[62,564,565],{"class":72}," :",[62,567,550],{"class":68},[62,569,139],{"class":94},[62,571,98],{"class":68},[62,573,144],{"class":117},[62,575,105],{"class":68},[62,577,578],{"class":64,"line":128},[62,579,247],{"class":68},[62,581,582],{"class":64,"line":244},[62,583,254],{"emptyLinePlaceholder":253},[62,585,586,588,590,593,596,599,601,604,606,609,612],{"class":64,"line":250},[62,587,294],{"class":68},[62,589,79],{"class":72},[62,591,592],{"class":94}," firstOrNull",[62,594,595],{"class":68},"(List.",[62,597,598],{"class":94},"of",[62,600,98],{"class":68},[62,602,603],{"class":101},"\"a\"",[62,605,406],{"class":68},[62,607,608],{"class":101},"\"b\"",[62,610,611],{"class":68},")); ",[62,613,614],{"class":124},"\u002F\u002F T inferred as String\n",[15,616,617,618,621,622,624,625,628],{},"The ",[19,619,620],{},"\u003CT>"," between the modifiers and the return type is what makes the method generic —\nwithout it, ",[19,623,183],{}," would be an undefined symbol. Crucially, a generic method's type parameter\nis ",[28,626,627],{},"independent"," of any type parameter on the enclosing class, which is what lets a\nplain utility class expose generic helpers without itself being generic.",[10,630,632],{"id":631},"type-inference-and-the-diamond-operator","Type inference and the diamond operator",[15,634,635,636,639,640,643,644,647,648,651,652,655],{},"You rarely have to write type arguments by hand because the compiler performs ",[28,637,638],{},"type\ninference",": it looks at the ",[28,641,642],{},"argument types"," and the ",[28,645,646],{},"target type"," (what the result\nis assigned to) and deduces the type parameter for you. The most visible form is the\n",[28,649,650],{},"diamond operator"," ",[19,653,654],{},"\u003C>"," (Java 7+), which lets you omit the type arguments on the right of\nan assignment and infer them from the declared type on the left.",[53,657,659],{"className":55,"code":658,"language":57,"meta":58,"style":58},"\u002F\u002F verbose, pre-Java 7\nMap\u003CString, List\u003CInteger>> m = new HashMap\u003CString, List\u003CInteger>>();\n\n\u002F\u002F diamond — compiler infers \u003CString, List\u003CInteger>> from the left side\nMap\u003CString, List\u003CInteger>> m2 = new HashMap\u003C>();\n",[19,660,661,666,698,702,707],{"__ignoreMap":58},[62,662,663],{"class":64,"line":65},[62,664,665],{"class":124},"\u002F\u002F verbose, pre-Java 7\n",[62,667,668,671,673,676,679,682,684,686,689,691,693,695],{"class":64,"line":88},[62,669,670],{"class":68},"Map\u003C",[62,672,73],{"class":72},[62,674,675],{"class":68},", List\u003C",[62,677,678],{"class":72},"Integer",[62,680,681],{"class":68},">> m ",[62,683,79],{"class":72},[62,685,82],{"class":72},[62,687,688],{"class":68}," HashMap\u003C",[62,690,73],{"class":72},[62,692,675],{"class":68},[62,694,678],{"class":72},[62,696,697],{"class":68},">>();\n",[62,699,700],{"class":64,"line":108},[62,701,254],{"emptyLinePlaceholder":253},[62,703,704],{"class":64,"line":128},[62,705,706],{"class":124},"\u002F\u002F diamond — compiler infers \u003CString, List\u003CInteger>> from the left side\n",[62,708,709,711,713,715,717,720,722,724],{"class":64,"line":244},[62,710,670],{"class":68},[62,712,73],{"class":72},[62,714,675],{"class":68},[62,716,678],{"class":72},[62,718,719],{"class":68},">> m2 ",[62,721,79],{"class":72},[62,723,82],{"class":72},[62,725,726],{"class":68}," HashMap\u003C>();\n",[15,728,729,730,733,734,737,738,741,742,745,746,749,750,753,754,757],{},"This cuts boilerplate without giving up any safety — ",[19,731,732],{},"new HashMap\u003C>()"," is still fully\ngeneric, just inferred. When inference genuinely can't decide (typically when there are no\narguments to learn from), you can supply an explicit ",[28,735,736],{},"type witness"," before the method\nname, as in ",[19,739,740],{},"Collections.\u003CString>emptyList()",". One caution with ",[19,743,744],{},"var",": the right-hand side\nmust carry the type arguments, because there's no left-hand declaration to borrow from —\nwrite ",[19,747,748],{},"new ArrayList\u003CString>()",", not ",[19,751,752],{},"new ArrayList\u003C>()",", or you'll infer\n",[19,755,756],{},"ArrayList\u003CObject>"," by accident.",[10,759,761],{"id":760},"raw-types-and-why-to-avoid-them","Raw types and why to avoid them",[15,763,764,765,768,769,772,773,775,776,779,780,783,784,787],{},"A ",[28,766,767],{},"raw type"," is a generic type used ",[28,770,771],{},"without"," its type argument — ",[19,774,25],{}," instead of\n",[19,777,778],{},"List\u003CString>",". It exists only for ",[28,781,782],{},"backward compatibility"," with pre-Java-5 code, and\nusing one ",[28,785,786],{},"opts out of type safety",": the compiler stops checking element types and\nre-introduces exactly the runtime failures generics were built to prevent.",[53,789,791],{"className":55,"code":790,"language":57,"meta":58,"style":58},"List raw = new ArrayList();      \u002F\u002F raw type — unchecked\nraw.add(\"hi\");\nraw.add(42);                     \u002F\u002F no complaint — types not checked\nString s = (String) raw.get(1);  \u002F\u002F ClassCastException at runtime\n",[19,792,793,811,824,840],{"__ignoreMap":58},[62,794,795,798,800,802,805,808],{"class":64,"line":65},[62,796,797],{"class":68},"List raw ",[62,799,79],{"class":72},[62,801,82],{"class":72},[62,803,804],{"class":94}," ArrayList",[62,806,807],{"class":68},"();      ",[62,809,810],{"class":124},"\u002F\u002F raw type — unchecked\n",[62,812,813,816,818,820,822],{"class":64,"line":88},[62,814,815],{"class":68},"raw.",[62,817,95],{"class":94},[62,819,98],{"class":68},[62,821,286],{"class":101},[62,823,105],{"class":68},[62,825,826,828,830,832,834,837],{"class":64,"line":108},[62,827,815],{"class":68},[62,829,95],{"class":94},[62,831,98],{"class":68},[62,833,118],{"class":117},[62,835,836],{"class":68},");                     ",[62,838,839],{"class":124},"\u002F\u002F no complaint — types not checked\n",[62,841,842,844,846,849,851,853,856,858],{"class":64,"line":128},[62,843,294],{"class":68},[62,845,79],{"class":72},[62,847,848],{"class":68}," (String) raw.",[62,850,139],{"class":94},[62,852,98],{"class":68},[62,854,855],{"class":117},"1",[62,857,147],{"class":68},[62,859,860],{"class":124},"\u002F\u002F ClassCastException at runtime\n",[15,862,863,864,867,868,871,872,875,876,878],{},"Raw types produce ",[28,865,866],{},"unchecked warnings",", which are the compiler begging you to\nparameterize. If you genuinely want a collection that holds anything, use ",[19,869,870],{},"List\u003CObject>","\ninstead — it keeps full checking on. Note that ",[19,873,874],{},"new HashMap()"," (no diamond) is a raw type,\nwhile ",[19,877,732],{}," is not; the missing brackets are not a cosmetic detail.",[10,880,882],{"id":881},"generic-invariance","Generic invariance",[15,884,885,886,888,889,406,891,898,899,902],{},"Here is the subtlety that trips up most people: even though ",[19,887,73],{}," is a subtype of\n",[19,890,21],{},[28,892,893,895,896],{},[19,894,778],{}," is not a subtype of ",[19,897,870],{},". Generics are\n",[28,900,901],{},"invariant",". This feels wrong until you see what it prevents.",[53,904,906],{"className":55,"code":905,"language":57,"meta":58,"style":58},"List\u003CString> strings = new ArrayList\u003C>();\nList\u003CObject> objs = strings;   \u002F\u002F compile error — and a good thing too\nobjs.add(42);                  \u002F\u002F ...because this would slip an Integer in\nString s = strings.get(0);     \u002F\u002F ...and this would explode at runtime\n",[19,907,908,923,940,957],{"__ignoreMap":58},[62,909,910,912,914,917,919,921],{"class":64,"line":65},[62,911,69],{"class":68},[62,913,73],{"class":72},[62,915,916],{"class":68},"> strings ",[62,918,79],{"class":72},[62,920,82],{"class":72},[62,922,85],{"class":68},[62,924,925,927,929,932,934,937],{"class":64,"line":88},[62,926,69],{"class":68},[62,928,21],{"class":72},[62,930,931],{"class":68},"> objs ",[62,933,79],{"class":72},[62,935,936],{"class":68}," strings;   ",[62,938,939],{"class":124},"\u002F\u002F compile error — and a good thing too\n",[62,941,942,945,947,949,951,954],{"class":64,"line":108},[62,943,944],{"class":68},"objs.",[62,946,95],{"class":94},[62,948,98],{"class":68},[62,950,118],{"class":117},[62,952,953],{"class":68},");                  ",[62,955,956],{"class":124},"\u002F\u002F ...because this would slip an Integer in\n",[62,958,959,961,963,966,968,970,972,975],{"class":64,"line":128},[62,960,294],{"class":68},[62,962,79],{"class":72},[62,964,965],{"class":68}," strings.",[62,967,139],{"class":94},[62,969,98],{"class":68},[62,971,144],{"class":117},[62,973,974],{"class":68},");     ",[62,976,977],{"class":124},"\u002F\u002F ...and this would explode at runtime\n",[15,979,980,981,984,985,988,989,992,993,996,997,1000,1001,1004],{},"The compiler blocks the assignment so that the unsafe sequence can never happen. Arrays,\nby contrast, ",[28,982,983],{},"are"," covariant — ",[19,986,987],{},"String[]"," is an ",[19,990,991],{},"Object[]"," — which is precisely why array\nstores are checked at runtime and can throw ",[19,994,995],{},"ArrayStoreException",". Generics chose\ncompile-time safety over array-style covariance, and wildcards (",[19,998,999],{},"List\u003C?>",", a list of some\n",[499,1002,1003],{},"unknown"," type) exist to recover the flexibility invariance gives up.",[10,1006,1008],{"id":1007},"multiple-type-parameters","Multiple type parameters",[15,1010,1011,1012,1015,1016,1019],{},"A generic type can take more than one parameter — just list them comma-separated. The\ncanonical example is ",[19,1013,1014],{},"Map\u003CK, V>",", parameterized by both a key type and a value type, but\nyou'll write your own ",[19,1017,1018],{},"Pair","-style carriers all the time.",[53,1021,1023],{"className":55,"code":1022,"language":57,"meta":58,"style":58},"class Pair\u003CA, B> {\n  final A first;\n  final B second;\n  Pair(A a, B b) { first = a; second = b; }\n}\n\nPair\u003CString, Integer> p = new Pair\u003C>(\"age\", 30);\nString key = p.first;   \u002F\u002F A = String\nint val = p.second;     \u002F\u002F B = Integer\n",[19,1024,1025,1045,1053,1060,1090,1094,1098,1129,1142],{"__ignoreMap":58},[62,1026,1027,1029,1032,1034,1037,1039,1042],{"class":64,"line":65},[62,1028,174],{"class":72},[62,1030,1031],{"class":94}," Pair",[62,1033,180],{"class":68},[62,1035,1036],{"class":72},"A",[62,1038,406],{"class":68},[62,1040,1041],{"class":72},"B",[62,1043,1044],{"class":68},"> {\n",[62,1046,1047,1050],{"class":64,"line":88},[62,1048,1049],{"class":72},"  final",[62,1051,1052],{"class":68}," A first;\n",[62,1054,1055,1057],{"class":64,"line":108},[62,1056,1049],{"class":72},[62,1058,1059],{"class":68}," B second;\n",[62,1061,1062,1065,1068,1071,1074,1077,1080,1082,1085,1087],{"class":64,"line":128},[62,1063,1064],{"class":94},"  Pair",[62,1066,1067],{"class":68},"(A ",[62,1069,1070],{"class":214},"a",[62,1072,1073],{"class":68},", B ",[62,1075,1076],{"class":214},"b",[62,1078,1079],{"class":68},") { first ",[62,1081,79],{"class":72},[62,1083,1084],{"class":68}," a; second ",[62,1086,79],{"class":72},[62,1088,1089],{"class":68}," b; }\n",[62,1091,1092],{"class":64,"line":244},[62,1093,247],{"class":68},[62,1095,1096],{"class":64,"line":250},[62,1097,254],{"emptyLinePlaceholder":253},[62,1099,1100,1103,1105,1107,1109,1112,1114,1116,1119,1122,1124,1127],{"class":64,"line":257},[62,1101,1102],{"class":68},"Pair\u003C",[62,1104,73],{"class":72},[62,1106,406],{"class":68},[62,1108,678],{"class":72},[62,1110,1111],{"class":68},"> p ",[62,1113,79],{"class":72},[62,1115,82],{"class":72},[62,1117,1118],{"class":68}," Pair\u003C>(",[62,1120,1121],{"class":101},"\"age\"",[62,1123,406],{"class":68},[62,1125,1126],{"class":117},"30",[62,1128,105],{"class":68},[62,1130,1131,1134,1136,1139],{"class":64,"line":275},[62,1132,1133],{"class":68},"String key ",[62,1135,79],{"class":72},[62,1137,1138],{"class":68}," p.first;   ",[62,1140,1141],{"class":124},"\u002F\u002F A = String\n",[62,1143,1144,1147,1150,1152,1155],{"class":64,"line":291},[62,1145,1146],{"class":72},"int",[62,1148,1149],{"class":68}," val ",[62,1151,79],{"class":72},[62,1153,1154],{"class":68}," p.second;     ",[62,1156,1157],{"class":124},"\u002F\u002F B = Integer\n",[15,1159,1160,1161,1163,1164,445,1167,1170],{},"Each parameter is ",[28,1162,627],{},", so ",[19,1165,1166],{},"Pair\u003CString, Integer>",[19,1168,1169],{},"Pair\u003CInteger, String>","\nare distinct, incompatible types. The compiler tracks every slot separately, which is what\nmakes a two-parameter container as type-safe as a one-parameter one.",[10,1172,1174],{"id":1173},"generic-constructors-and-static-methods","Generic constructors and static methods",[15,1176,1177,1178,1181],{},"Two corners surprise people. First, a ",[28,1179,1180],{},"constructor can declare its own type parameter",",\nindependent of the class's, written before the constructor name. This is rare but useful\nwhen the constructor needs a type purely for its arguments.",[53,1183,1185],{"className":55,"code":1184,"language":57,"meta":58,"style":58},"class Holder\u003CT> {\n  private T value;\n  \u002F\u002F S is the constructor's own parameter, separate from class T\n  \u003CS extends T> Holder(S initial) { this.value = initial; }\n}\n",[19,1186,1187,1200,1206,1211,1250],{"__ignoreMap":58},[62,1188,1189,1191,1194,1196,1198],{"class":64,"line":65},[62,1190,174],{"class":72},[62,1192,1193],{"class":94}," Holder",[62,1195,180],{"class":68},[62,1197,183],{"class":72},[62,1199,1044],{"class":68},[62,1201,1202,1204],{"class":64,"line":88},[62,1203,194],{"class":72},[62,1205,197],{"class":68},[62,1207,1208],{"class":64,"line":108},[62,1209,1210],{"class":124},"  \u002F\u002F S is the constructor's own parameter, separate from class T\n",[62,1212,1213,1216,1218,1221,1224,1227,1230,1233,1236,1239,1242,1245,1247],{"class":64,"line":128},[62,1214,1215],{"class":68},"  \u003C",[62,1217,467],{"class":72},[62,1219,1220],{"class":72}," extends",[62,1222,1223],{"class":72}," T",[62,1225,1226],{"class":68},"> ",[62,1228,1229],{"class":94},"Holder",[62,1231,1232],{"class":68},"(S ",[62,1234,1235],{"class":214},"initial",[62,1237,1238],{"class":68},") { ",[62,1240,1241],{"class":117},"this",[62,1243,1244],{"class":68},".value ",[62,1246,79],{"class":72},[62,1248,1249],{"class":68}," initial; }\n",[62,1251,1252],{"class":64,"line":244},[62,1253,247],{"class":68},[15,1255,1256,1257,1260,1261,1263,1264,1267],{},"Second, a ",[28,1258,1259],{},"static method cannot see the class's type parameter",", because that parameter\nexists per instance and a static method belongs to the class. A static method that needs\ngenerics must declare its ",[28,1262,496],{}," — which is exactly how factory methods like\n",[19,1265,1266],{},"List.of"," and the pattern below work.",[53,1269,1271],{"className":55,"code":1270,"language":57,"meta":58,"style":58},"class Box\u003CT> {\n  T value;\n  \u002F\u002F static \u003CU> needed — the class's T is not in scope here\n  static \u003CU> Box\u003CU> of(U v) {\n    Box\u003CU> b = new Box\u003C>();\n    b.value = v;\n    return b;\n  }\n}\n\nBox\u003CString> b = Box.of(\"hi\");   \u002F\u002F U inferred as String\n",[19,1272,1273,1285,1290,1295,1321,1336,1346,1354,1359,1363,1368],{"__ignoreMap":58},[62,1274,1275,1277,1279,1281,1283],{"class":64,"line":65},[62,1276,174],{"class":72},[62,1278,177],{"class":94},[62,1280,180],{"class":68},[62,1282,183],{"class":72},[62,1284,1044],{"class":68},[62,1286,1287],{"class":64,"line":88},[62,1288,1289],{"class":68},"  T value;\n",[62,1291,1292],{"class":64,"line":108},[62,1293,1294],{"class":124},"  \u002F\u002F static \u003CU> needed — the class's T is not in scope here\n",[62,1296,1297,1300,1302,1304,1307,1309,1311,1313,1316,1318],{"class":64,"line":128},[62,1298,1299],{"class":72},"  static",[62,1301,520],{"class":68},[62,1303,473],{"class":72},[62,1305,1306],{"class":68},"> Box\u003C",[62,1308,473],{"class":72},[62,1310,1226],{"class":68},[62,1312,598],{"class":94},[62,1314,1315],{"class":68},"(U ",[62,1317,215],{"class":214},[62,1319,1320],{"class":68},") {\n",[62,1322,1323,1326,1328,1330,1332,1334],{"class":64,"line":244},[62,1324,1325],{"class":68},"    Box\u003C",[62,1327,473],{"class":72},[62,1329,265],{"class":68},[62,1331,79],{"class":72},[62,1333,82],{"class":72},[62,1335,272],{"class":68},[62,1337,1338,1341,1343],{"class":64,"line":250},[62,1339,1340],{"class":68},"    b.value ",[62,1342,79],{"class":72},[62,1344,1345],{"class":68}," v;\n",[62,1347,1348,1351],{"class":64,"line":257},[62,1349,1350],{"class":72},"    return",[62,1352,1353],{"class":68}," b;\n",[62,1355,1356],{"class":64,"line":275},[62,1357,1358],{"class":68},"  }\n",[62,1360,1361],{"class":64,"line":291},[62,1362,247],{"class":68},[62,1364,1366],{"class":64,"line":1365},10,[62,1367,254],{"emptyLinePlaceholder":253},[62,1369,1371,1373,1375,1377,1379,1382,1384,1386,1388,1391],{"class":64,"line":1370},11,[62,1372,260],{"class":68},[62,1374,73],{"class":72},[62,1376,265],{"class":68},[62,1378,79],{"class":72},[62,1380,1381],{"class":68}," Box.",[62,1383,598],{"class":94},[62,1385,98],{"class":68},[62,1387,286],{"class":101},[62,1389,1390],{"class":68},");   ",[62,1392,1393],{"class":124},"\u002F\u002F U inferred as String\n",[15,1395,1396,1397,1399],{},"Reusing the class's letter ",[19,1398,183],{}," for a static or constructor parameter is legal but\nconfusing — pick a different letter so readers know it's a separate, independently-scoped\ntype.",[10,1401,1403],{"id":1402},"recap","Recap",[15,1405,1406,1408,1409,1411,1412,1415,1416,1418,1419,1422,1423,1425,1426,1422,1428,1430,1431,1434,1435,1437,1438,1448,1449,1452,1453,1455,1456,1458,1459,1461,1462,1464,1465,1468],{},[28,1407,38],{}," parameterize code by type to deliver ",[28,1410,46],{}," and\n",[28,1413,1414],{},"cast-free reads"," — type errors become build errors instead of ",[19,1417,34],{},"s.\nDeclare a ",[28,1420,1421],{},"generic class"," with ",[19,1424,620],{}," after the name, a ",[28,1427,492],{},[19,1429,620],{},"\nbefore the return type (independent of the class), and lean on ",[28,1432,1433],{},"type inference"," and the\n",[28,1436,650],{}," so you rarely write type arguments by hand. Follow the\n",[28,1439,1440,468,1442,468,1444,468,1446],{},[19,1441,183],{},[19,1443,439],{},[19,1445,403],{},[19,1447,409],{}," naming conventions for readable signatures, and never fall back to\n",[28,1450,1451],{},"raw types"," — use ",[19,1454,870],{}," if you truly want anything. Remember that generics are\n",[28,1457,901],{}," (",[19,1460,778],{}," is not a ",[19,1463,870],{},"), that multiple type parameters are\nindependent, and that ",[28,1466,1467],{},"static methods and constructors"," declare their own type\nparameters. Get these basics right and the rest of generics — wildcards, bounds, and\nerasure — builds cleanly on top.",[1470,1471,1472],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":58,"searchDepth":88,"depth":88,"links":1474},[1475,1476,1477,1478,1479,1480,1481,1482,1483,1484],{"id":12,"depth":88,"text":13},{"id":156,"depth":88,"text":157},{"id":377,"depth":88,"text":378},{"id":485,"depth":88,"text":486},{"id":631,"depth":88,"text":632},{"id":760,"depth":88,"text":761},{"id":881,"depth":88,"text":882},{"id":1007,"depth":88,"text":1008},{"id":1173,"depth":88,"text":1174},{"id":1402,"depth":88,"text":1403},"A practical guide to Java generics — why they exist, how to write generic classes and methods, type-parameter conventions, the diamond operator, raw types, generic invariance, and generic constructors.","medium","md","Java",{},"\u002Fblog\u002Fjava-generics-basics-type-safety","\u002Fjava\u002Fgenerics\u002Fgenerics-basics",{"title":5,"description":1485},"blog\u002Fjava-generics-basics-type-safety","Generics Basics","generics","2026-06-20","tWiLIoa-IgqoOuaIauxlGkV1JoaLuTC-VvkX0oZh9UQ",1782244089387]