Thetwist
|_Method-callbinding
Connectingamethodcalltoamethodbodyiscalledbinding.Whenbindingisperformedbeforethe Whenalanguageimplementslatebinding,theremustbesomemechanismtodeterminethetypeoftheobjectatruntimeandtocalltheappropriatemethod.Thatis,thecompilerstilldoesn'tknowtheobjecttype,butthemethod-callmechanismfindsoutandcallsthecorrectmethodbody.Thelate-bindingmechanismvariesfromlanguagetolanguage,butyoucanimaginethesomesortoftypeinformationmustbeinstalledintheobjects. Allmethodbindinginjavauseslatebindingunlessthemethodisstaticorfinal(privatemethodareimplicitlyfinal).Thismeansthatordinarilyyoudon'tneedtomakeanydecisionaboutwhetherlatebindingwilloccur-ithappensautomatically. |_Pitfall:"overriding"privatemethods Here'ssomethingyoumightinnocentlytrytodo: Youmightreasonablyexpecttheoutputtobe"publicf()",butaprivatemethodisautomaticallyfinal,andisalsohiddenfromthederivedclass.SoDerived'sf()inthecaseisabrandnewmethod;it'snotevenoverloaded,sincethebase-classversionoff()isn'tvisibleinDerived.Ifyouwanttogetthe"publicf()",youcanmodifythatcodepo.f()to((Derived)po).f(). Theresultofthisisthatonlynon-privatemethodsmaybeoverridden,butyoushouldwatchoutfortheappearanceofoverridingprivatemethods,whichgeneratesnocompilerwarnings,butdoesn'tdowhatyoumightexpect.Tobeclear,youshoulduseadifferentnamefromaprivatebase-classmethodinyourderivedclass. |_Pitfall:fieldsandstaticmethods Onceyoulearnaboutpolymorphism,youcanbegintothinkthateverythinghappenspolymorphically.However,onlyordinarymethodcallscanpolymorphic.Ifyouaccesspublic class PrivateOverride{private void f() {System.out.println("private f()");}public static void main(String[] args){PrivateOverride po = new Derived();po.f(); }}class Derived extends PrivateOverride{public void f() {System.out.println("public f()");}}/*Output:private f()*/
Althoughthisseemslikeitcouldbeaconfusingissue,inpracticeitvirtuallynevercomesup.Foronething,you'llgenerallymakeallfieldsprivateandsoyouwon'taccessthemdirectly,butonlyassideeffectsofcallingmethods,Inaddition,youprobablywon'tgivethesamenametobase-classfieldandaderived-classfield,becauseitsconfusing.
Ifamethodisstatic,itdoesn'tbehavepolymorphically,staticmethodsareassociatedwiththeclass,andnottheindividualobjects.
Constructorsandpolymorphism
|_Orderofconstructorcalls
Eventhoughconstructorsarenotpolymorphic(they'reactuallystaticmethods,butthestaticdeclarationisimplicit),it'simportanttounderstandthewayconstructorsworkincomplexhierarchiesandwithpolymorphism.
Aconstructorforthebaseclassisalwayscalledduringtheconstructionprocessforaderivedclass,chaininguptheinheritancehierarchysothataconstructorforeverybaseclassiscalled.
Theorderofconstructorcallsforacomplexobjectisasfollows:
1.Thebase-classconstructoriscalled.Thisstepisrepeatedrecursivelysuchthattherootofthehierarchyisconstructedfirst,followedbythenext-derivedclass,etc.,untilthemost-derivedclassisreached.
2.Memberinitializersarecalledintheorderofdeclaration.
3.Thebodyofthederived-classconstructoriscalled.
Theorderoftheconstructorcallsisimportant.Whenyouinherit,youknowallaboutthebaseclassandcanaccessanypublicandprotectedmembersofthebaseclass.Thismeansthatyoumustbeabletoassumethatallthemembersofthebaseclassarevalidwhenyou'remembersofallpartsoftheobjecthavebeenbuilt.Insidetheconstructor,however,youmustbeabletoassumethatallmembersthatyouusehavabeenbuilt.Theonlywaytoguaranteethisisforthebase-classconstructortobecalledfirst.Thenwhenyou'reinthederived-classconstructor,allthemembersyoucanaccessinthebaseclasshavebeeninitialized.Knowingthatallmembersarevalidinsidetheconstructorisalsothereasonthat,wheneverpossible,youshouldinitializeallmemberobject(thatis,objectsplacedintheclassusingcomposition)attheirpointofdefinitionintheclass.Ifyoufollowthispractice,youwillheapensurethatallbaseclassmembersandmembersobjectsofthecurrentobjecthavebeeninitialized.
|_Inheritanceandcleanup
Whenyouoverridedispose()(thenameIhavechosentousehere;youmaycomeupwithsomethingbetter)inaninheritedclass,it'simportanttoremembertocallthebase-classversionofdispose(),sinceotherwisethebase-classcleanupwillnothappen.
|_Behaviorofpolymorphicmethodsinsideconstructors
Conceptually,theconstructor'sjobistobringtheobjectintoexistence(whichishardlyanordinaryfeat).Insideanyconstructor,theentireobjectmightbeonlypartiallyformed-youcanonlyknowthatthebase-classobjectshavebeeninitialized.Iftheconstructorisonlyonestepinbuildinganobjectofaclassthat'sbeenderivedfromthatconstructor'sclass,thederivedpartshavenotyetbeeninitializedatthetimethatthecurrentconstructorisbeingcalled.Adynamicboundmethodcall,however,reaches"outward"intotheinheritancehierarchy.Itcallsamethodinaderivedclass.Ifyoudothisinsideaconstructor,youcallamethodthatmightman Youcanseetheprobleminthefollowingexample: Theorderofinitializationdescribedintheearliersectionisn'tquitecomplete,andthat'sthekeytosolvingthemystery.Theactualprocessofinitializationis: 1.Thestorageallocatedfortheobjectisinitializedtobinaryzerobeforeanythingelsehappens. 2.Thebase-classconstructorsarecalledasdescribedpreviously.Atthispoint,theoverriddendraw()methodiscalled(yes,beforetheRoundGlyphconstructoriscalled),whichdiscoversaradiusvalueofzero,duetoStep1. 3.Memberinitializesarecalledintheorderofdeclaration. 4.Thebodyofthederived-classconstructoriscalled. Foravoidthisproblem,agoodguidelineforguidelineforconstructorsis,"Doaslittleaspossibletosettheobjectintoagoodstate,andifyoucanpossiblyavoidit,don'tcallanyothermethodsinthisclass."Theonlysafemethodstocallinsideaconstructorarethosethatarefinalinthebaseclass.(Thisalsoappliestoprivatemethods,whichareautomaticallyfinal.)Thesecannotbeoverriddenandthuscannotproducethiskindofsurprise.Youmaynotalwaysbeabletofollowthisguideline,butit'ssomethingtostrivetowards. Covariantreturntypes JavaSE5addscovariantreturntypes,whichmeansthatanoverriddenmethodinaderivedclasscanreturnatypederivedfromthetypereturnedbythebase-classmethod. Designingwithinheritance Ageneralguidelineis"Useinheritancetoexpressdifferencesinbehavior,andfieldstoexpressvariationsinstate". (END_XPJIANG)class Glyph{void draw() {System.out.println("Glyph.draw()");}Glyph(){System.out.println("Glyph() before draw()");draw();System.out.println("Glyph() after draw()");}}class RoundGlyph extends Glyph{private int radius = 1;RoundGlyph(int r){radius = r;System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);}void draw(){System.out.println("RoundGlyph.draw(), radius = " + radius);}}public class PolyConstructors{public static void main(String[] args){new RoundGlyph(5);}}/* Output:Glyph() before draw()RoundGlyph.draw(), radius = 0Glyph() after draw()RoundGlyph.RoundGlyph(), radius = 5*/
新聞熱點
疑難解答