首页 > 学院 > 开发设计 > 正文

TIJ英文原版书籍阅读之旅——Chapter Eight:Polymorphism

2019-11-15 00:36:06
字体:
来源:转载
供稿:网友
TIJ英文原版书籍阅读之旅——Chapter Eight:Polymorphism 2015-06-16 10:25 by 海峰 :), ... 阅读, ... 评论, 收藏, 编辑

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)


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表