More than you ever wanted to know about Java Generics€¦ · Fruits – So it can contain Fruits...

Preview:

Citation preview

Morethanyoueverwantedtoknowabout

JavaGenericsJeffMeister

CMSC420Summer2007

GENERICS:AYOUNGLADY’SILLUSTRATEDPRIMERINFOURSLIDES

Theobligatoryreviewoftheboringstuff,or…

Java1.4:LifeBeforeGenerics

Javacodeusedtolooklikethis:

ListlistOfFruits=newArrayList();listOfFruits.add(newFruit(“Apple”));Fruitapple=(Fruit)listOfFruits.remove(0);listOfFruits.add(newVegetable(“Carrot”));//Whoops!Fruitorange=(Fruit)listOfFruits.remove(0);//Run‐timeerror

Problem:Compilerdoesn’tknowlistOfFruits shouldonlycontainfruits

ASillySoluMon

Wecouldmakeourownfruit‐onlylistclass:

classFruitList{voidadd(Fruitelement){…}Fruitremove(intindex){…}…}

Butwhataboutwhenwewantavegetable‐onlylistlater?Copy‐paste?Lotsofbloated,unmaintainablecode?

Java1.5:NowWe’reTalking

Now,Javacodelookslikethis:

List<Fruit>listOfFruits=newArrayList<Fruit>();listOfFruits.add(newFruit(“Apple”));Fruitapple=listOfFruits.remove(0);listOfFruits.add(newVegetable(“Carrot”));//Compile‐timeerror

Hooray!CompilernowknowslistOfFruitscontainsonlyFruits

•  Soremove()mustreturnaFruit•  Andadd()cannottakeaVegetable

Youguysrememberthis,right?

Here’showwe’dwritethatgenericListclass:

classList<T>{voidadd(Telement){…}Tremove(intindex){…}…}

Problemsolved!SimplyinvokeList<Fruit>,List<Vegetable>,andsoon.I’msureyou’vewri^encodelikethisbefore,solet’smoveto…

THEFUNSTUFFHopeyouplannedonbeingconfusedtoday,becauseit’sMmefor…

AbandonAllHope…

•  Genericsimplementparametricpolymorphism– Parametric:Thetypeparameter(e.g.,<T>)…– Polymorphism:…cantakemanyforms

•  However,ifwe’regoingtoprogramwithparameterizedtypes,weneedtounderstandhowthelanguagerulesapplytothem

•  Javagenericsareimplementedusingtypeerasure,whichleadstoallsortsofwackyissues,aswe’llsee

Subtyping

SinceAppleisasubtypeofObject,isList<Apple>asubtypeofList<Object>?

List<Apple>apples=newArrayList<Apple>();List<Object>objs=apples;//Doesthiscompile?

Seemsharmless,butno!Ifthatworked,wecouldputOrangesinourList<Apple>likeso:

objs.add(newOrange());//OKbecauseobjsisaList<Object>Applea=apples.remove(0);//WouldassignOrangetoApple!

AnAside:SubtypingandJavaArrays

•  Javaarraysactuallyhavethesubtypingproblemjustdescribed(theyarecovariant)

•  Thefollowingobviouslywrongcodecompiles,onlytofailatrun‐Mme:

Apple[]apples=newApple[3];Object[]objs=apples;//Thecompilerpermitsthis!objs[0]=newOrange();//ArrayStoreException

•  Avoidmixingarraysandgenerics(trustme)

WildcardTypes

•  So,whatisList<Apple>asubtypeof?•  ThesupertypeofallkindsoflistsisList<?>,theListofunknown

•  The?isawildcardthatmatchesanything

•  Wecan’taddthings(exceptnull)toaList<?>,sincewedon’tknowwhattheListisreallyof

•  ButwecanretrievethingsandtreatthemasObjects,sinceweknowtheyareatleastthat

BoundedWildcards

•  Wildcardtypescanhaveupperandlowerbounds

•  AList<?extendsFruit>isaListofitemsthathaveunknowntypebutareallatleastFruits– SoitcancontainFruitsandApplesbutnotPeas

•  AList<?superFruit>isaListofitemsthathaveunknowntypebutareallatmostFruits– SoitcancontainFruitsandObjectsbutnotApples

BoundedWildcardsExampleclassWholesaleVendor<T>{voidbuy(inthowMany,List<?superT>fillMeUp){…}voidsell(List<?extendsT>emptyMe){…}…}

WholesaleVendor<Fruit>vendor=newWholesaleVendor<Fruit>();List<Food>stock=…;List<Apple>overstockApples=…;

//IcanbuyFoodfromtheFruitvendor:vendor.buy(100,stock);//IcansellmyApplestotheFruitvendor:vendor.sell(overstockApples);

JoshBloch’sBoundedWildcardsRule

•  Use<?extendsT>whenparameterizedinstanceisaTproducer(forreading/input)

•  Use<?superT>whenparameterizedinstanceisaTconsumer(forwriMng/output)

Uhh…what?

GenericMethods

•  Youcanparameterizemethodstoo.Here’sasignaturefromtheJavaAPI:

static<T>voidfill(List<?superT>list,Tobj);

•  Easyenough,yeah?Trythisoneonforsize:static<TextendsObject&Comparable<?superT>>T

max(Collection<?extendsT>coll);

•  Youdon’tneedtoexplicitlyinstanMategenericmethods(thecompilerwillfigureitout)

HowGenericsareImplemented

•  RatherthanchangeeveryJVMbetweenJava1.4and1.5,theychosetouseerasure

•  Agerthecompilerdoesitstypechecking,itdiscardsthegenerics;theJVMneverseesthem!

•  Itworkssomethinglikethis:–  TypeinformaMonbetweenanglebracketsisthrownout,e.g.,List<String>List

– Usesoftypevariablesarereplacedbytheirupperbound(usuallyObject)

–  Castsareinsertedtopreservetype‐correctness

ProsandConsofErasure

•  Good:BackwardcompaMbilityismaintained,soyoucansMlluselegacynon‐genericlibraries

•  Bad:Youcan’tfindoutwhattypeagenericclassisusingatrun‐Mme:

classExample<T>{voidmethod(Objectitem){if(iteminstanceofT){…}//Compilererror!TanotherItem=newT();//Compilererror!T[]itemArray=newT[10];//Compilererror!}}

UsingLegacyCodeinGenericCode

•  SayIhavesomegenericcodedealingwithFruits,butIwanttocallthislegacylibraryfuncMon:

SmoothiemakeSmoothie(Stringname,Listfruits);

•  IcanpassinmygenericList<Fruit>forthefruitsparameter,whichhastherawtypeList.Butwhy?Thatseemsunsafe…makeSmoothie()couldsMckaVegetableinthelist,andthatwouldtastenasty!

RawTypesandGenericTypes

•  Listdoesn’tmeanList<Object>,becausethenwecouldn’tpassinaList<Fruit>(subtyping,remember?)

•  Listdoesn’tmeanList<?>either,becausethenwecouldn’tassignaListtoaList<Fruit>(whichisalegaloperaMon)

•  Weneedbothofthesetoworkforgenericcodetointeroperatewithlegacycode

•  Rawtypesbasicallyworklikewildcardtypes,justnotcheckedasstringently–  TheseoperaMonsgenerateanuncheckedwarning

TheProblemwithLegacyCode

•  “Callinglegacycodefromgenericcodeisinherentlydangerous;onceyoumixgenericcodewithnon‐genericlegacycode,allthesafetyguaranteesthatthegenerictypesystemusuallyprovidesarevoid.However,youaresMllbe^eroffthanyouwerewithoutusinggenericsatall.Atleastyouknowthecodeonyourendisconsistent.”–GiladBracha,JavaGenericsDeveloper

MyAdviceonGenerics

•  Don’ttrytothinkaboutgenericcodeabstractly;makeanexampleinstanMaMoninyourheadandrunthroughscenariosusingit

•  Genericsareavaluabletooltoensuretypesafety,sousethem!Letthecompilerhelpyou

•  However,genericsalsocomplicatesyntax,andtheycangeneratesomenastyerrorsthatareapaintounderstandanddebug

AnAnalogy:FuncMons

•  Problem:IwanttoperformthesamecomputaMononmanydifferentinputvalueswithoutwriMngthecomputaMonoverandover.

•  SoluMon:WriteafuncMon!Useavariabletorepresenttheinputvalue,andwriteyourcodetoperformthecomputaMononthisvariableinawaythatdoesnotdependonitsvalue.NowyoucancallthefuncMonmanyMmes,passingindifferentvaluesforthevariable.Easystuff.

GenericsProvideAnotherAbstracMon

•  Problem:Iwanttousethesameclass(ormethod)withobjectsofmanydifferenttypeswithoutwriMngtheclassoverandoverorsacrificingtypesafety.

•  SoluMon:Generifytheclass!UseavariableTtorepresenttheinputtype,andwriteyourcodetooperateonobjectsoftypeTinawaythatdoesnotdependontheactualvalueofT.NowyoucaninstanMatetheclassmanyMmes,passingindifferenttypesforT.

•  See?It’snotsobad.Genericsjustallowyoutoabstractovertypesinsteadofvalues.

Recommended