Thetwist
|_Method-callbinding
Connectingamethodcalltoamethodbodyiscalledbinding.WhenbindingisperformedbeforethePRogramisrun(bythecompilerandlinker,ifthereisone),it'scalledearlybinding.Youmightnothavaheardthetermbeforebecauseithasneverbeenanoptionwithprocedurallanguages.Ccompilershavaonlyonekindofmethodcall,andthat'searlybinding.
Whenalanguageimplementslatebinding,theremustbesomemechanismtodeterminethetypeoftheobjectatruntimeandtocalltheappropriatemethod.Thatis,thecompilerstilldoesn'tknowtheobjecttype,butthemethod-callmechanismfindsoutandcallsthecorrectmethodbody.Thelate-bindingmechanismvariesfromlanguagetolanguage,butyoucanimaginethesomesortoftypeinformationmustbeinstalledintheobjects.
Allmethodbindinginjavauseslatebindingunlessthemethodisstaticorfinal(privatemethodareimplicitlyfinal).Thismeansthatordinarilyyoudon'tneedtomakeanydecisionaboutwhetherlatebindingwilloccur-ithappensautomatically.
|_Pitfall:"overriding"privatemethods
Here'ssomethingyoumightinnocentlytrytodo:
public 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()*/
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.Ifyouaccessafielddirectly,thataccesswillberesolvedatcompiletime.
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,youcallamethodthatmightmanipulatemembersthathaven'tbeeninitializedyet-asurerecipefordisaster.
Youcanseetheprobleminthefollowingexample:
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*/
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)
新闻热点
疑难解答