chapter 7 inheritance - george mason universitykauffman/cs211/07-inheritance.pdf · chapter 7...

13
Chapter 7 Inheritance Class Inheritance: Introduction extends usage protected visibility abstract classes/methods Hello! Be sure to actually work through the examples in order; the "Your Turn" problems build upon each other extensively. Today we will cover another key OO concept: inheritance. We're comfortable with all the structure and hierarchy of creating separate classes, using fields to get some aggregation going, but our class definitions are still all written separately. Inheritance is going to allow us to write classes by essentially borrowing all of another class's definition, and selectively adding more fields and methods to it, with some nifty relationships holding between the original and newly-defined classes. Motivation So far, the only way we could cause two classes to interact in any way was through aggregation: where the object of one class "HAS-A" object of another class as part of its own data. Let's quickly review an example of aggregation, because we need to be sure we contrast the idea of aggregation with the new concept of inheritance. Aggregation Example: A Sphere HAS-A Coordinate: public class Coordinate { public double x,y,z; public Coordinate (int a, int b, int c) { x=a; y=b; z=c; } } public class Sphere { private Coordinate location; private in radius; public Sphere (int r, int x, int y, int z){ radius = r; location = new Coordinate(x,y,z); } }

Upload: doanphuc

Post on 28-May-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

Chapter7Inheritance

ClassInheritance:Introductionextendsusage

protectedvisibilityabstractclasses/methods

Hello!Besuretoactuallyworkthroughtheexamplesinorder;the"YourTurn"problemsbuilduponeachotherextensively.TodaywewillcoveranotherkeyOOconcept:inheritance.We'recomfortablewithallthestructureandhierarchyofcreatingseparateclasses,usingfieldstogetsomeaggregationgoing,butourclassdefinitionsarestillallwrittenseparately.Inheritanceisgoingtoallowustowriteclassesbyessentiallyborrowingallofanotherclass'sdefinition,andselectivelyaddingmorefieldsandmethodstoit,withsomeniftyrelationshipsholdingbetweentheoriginalandnewly-definedclasses.MotivationSofar,theonlywaywecouldcausetwoclassestointeractinanywaywasthroughaggregation:wheretheobjectofoneclass"HAS-A"objectofanotherclassaspartofitsowndata.Let'squicklyreviewanexampleofaggregation,becauseweneedtobesurewecontrasttheideaofaggregationwiththenewconceptofinheritance.AggregationExample:ASphereHAS-ACoordinate:public class Coordinate { public double x,y,z; public Coordinate (int a, int b, int c) { x=a; y=b; z=c; } } public class Sphere { private Coordinate location; private in radius; public Sphere (int r, int x, int y, int z){ radius = r; location = new Coordinate(x,y,z); } }

Aparticularspherehasitsownlocation.EachtimewecreateaSphereobject,wewillhaveaCoordinateobjectaswell.Althoughweseethataninstanceofoneclass(aSphere)hasareferencetoaninstanceofanotherclass(aCoordinate),neitherclassdefinitionisamorespecificversionoftheother.ASphereisn'taCoordinate,eventhoughaSpherehas-aCoordinate;aCoordinateisn'taSphere.Similarly,Sphereisn'tadouble,eventhoughitmayhaveonetorepresenttheradius.

InheritanceTheclassesthemselveseachrepresentatypethatisentirelyseparatefromanythingelse:ASphereobjecthas-aradius,andithas-aCoordinateobject,butnothingrelatesourSphereclasstoaShapeclassoraCubeclass.Wewanttostartidentifyingdifferentclassdefinitionsthatshouldsomehowbelinkedtogether,definedsomehowviathesamecode.WhenWouldIWantInheritance?Considerthefollowingthreeclassdefinitions.Wearecreatinga(verysimple)programtouseoncampus,andwanttostoreinformationaboutvariouspeopleoncampus.Wehavestudents,employees,andothernon-specificpersons.public class Person { private String name; private int age; //methods: (constructors), getName, setName, getAge, setAge. } public class Student { private String name, major; private int age, masonID, yearsOnCampus; /* methods: (constructors), getName, setName, getMajor, setMajor, getAge, setAge, getMasonID, setMasonID, getYearsOnCampus, setYearsOnCampus. */ }

public class Employee { private String name, jobTitle; private int age, masonID, yearsOnCampus; /* methods: (constructors), getName, setName, getJobTitle, setJobTitle, getAge, setAge, getMasonID, setMasonID, getYearsOnCampus, setYearsOnCampus. */ }

Weseesomeoverlapinthesedefinitions:alloftheseclasseshaveanameandage.SomeoftheclasseshavemasonIDandyearsOnCampusvariables.Andafewothervariablesareuniquetodifferentclasses(major,jobTitle).Nowimaginethatweaddedconstructors,getters,andsettersforeachoftheseinstancevariables.We'dseemoreandmoreduplicatedcode,bothindataandbehaviors(methods).Wecancreatemultipleobjectsfromeachoftheseclasses:Student s1 = new Student( "Bob", "art", 19, 71922, 2 ); Student s2 = new Student( "Helen", "education", 18, 71923, 1 ); Person[] people = { new Person("A",1) , new Person("B",2) , new Person("C",3) }; Employee e1 = new Employee ("Catherine", "cashier", 35, 71985, 7); Butthere'saproblem:wecan'tactuallyusethesedefinitionstogetherverywell.Supposewewantedtorepresentthepeopleinlineatthecashregisterinthefoodcourt:Person pers = new Person ("Sally", 25); Student stud = new Student ("Buddy", "undeclared", 21, 71234,2); Employee emp = new Employee ("Doug", "HR", 41, 72468, 10); SomeType[] customers = {pers, stud, emp}; // BAD CODE ????????

WhattypeshouldweputforSomeType?Wecan'tchooseanyofPerson,Student,Employee,becausethere'ssomethinginthearraythatdoesn'tmatchthetype.Theproblemisthelackofarelationbetweenthesedifferenttypes:thereisnowayforustotreataStudentlikeaPerson.EventhoughaStudenthasalltheinstancevariablesaPersonhas,andhasallthemethodsaPersonhas,wecan'ttreataStudentlikeaPerson.JavahasnoideathatitissafetotreataStudentlikeaPerson.Howimpersonal!Similarly,wecan'ttreatanEmployeelikeaPersoneither.DouginHRisgoingtohearmycomplaintsaboutthisworkenvironment.WewanttogiveJavatheinformationsothatthesedifferentclassesarelinkedtogether:wewanttobeabletosayaStudentIS-APerson,andthatanEmployeeIS-APerson.

So:wewanttorelatethesedifferentclassdefinitionsbyactuallydefiningoneclassintermsofanother.WewanttoreusethePersondefinition,andextendittotellJavahowaStudentisamore-specificversionofaPerson,andhowanEmployeeisamore-specificversionofaPerson.

public class Person { private String name; private int age; //methods: (constructors), getName, setName, getAge, setAge. } public class Student extends Person { // we inherit name and age. private String major; private int masonID, yearsOnCampus; /* methods that we write: (constructors), getMajor, setMajor, getMasonID, setMasonID, getYearsOnCampus, setYearsOnCampus. */ } public class Employee extends Person { // we inherit name and age. private String jobTitle; private int masonID, yearsOnCampus; /* methods: (constructors), getJobTitle, setJobTitle, getMasonID, setMasonID, getYearsOnCampus, setYearsOnCampus. */ }

Let'sstopandthinkamomentaboutourtwousesofextends Person.Student extends Person.Byaddingthosetwowords(extends Person),wearetellingJavathataStudentisaPerson.ThatmeansthatallthedataaPerson has,aStudent hasthatdata,too.AllthebehaviorsthataPersoncanexhibit,aStudentcanexhibittoo.Wedon'texplicitlylistString name andint age intheStudentclass,buteachStudentobjectwillhavetheseinstancevariables,becauseaStudentisaPersonandaPersonhasanameandage.Similarly,allthemethodsthatweredefinedinthePersonclass(e.g.,getName,setAge)arealsoavailabletoStudentobjects,becauseaStudentisaPerson.Whenwedrawthememoryusageforobjectsonthewhiteboardwithacircleencompassingallthedataandmethods,wewouldnowdrawaStudentobjectbydrawingaPersonobject,andthenaddinginthemajor,masonID,andyearsOnCampusinstancevariablestothedataportion(tophalf),andaddingtheextra

methoddefinitionstothebehaviorssection(bottomhalf).Again,notethatallwedowithsubclassesisadddefinitions,nevertakeaway.Thiscrucialonly-adding-thingspropertyiswhywe'reabletosaythateverysingleStudentobjectcanalwaysbeusedasaPersonobject.It'sasifwetemporarilyignoretheextravariablesandmethodsthatStudentshave,andjustrelyontheinheritedportions.

Inthediagram,weseethattheStudentobjecthasallthePersonfields(age,name),followedbytheextrafieldsspecifictoStudents(major,masonID,yearsOnCampus).Similarly,ithasallthePersonmethods,followedbyallthespecificStudentmethods(getters/settersformajor,masonID,andyearsOnCampus).IfweignoredpartsoftheStudentobject,itwouldlookjustlikeaPersonobject.ThisistheguaranteethatJavaisgivenwhenweextendaclasstocreateamorespecificversionofit.SomeTerminologyWehavealotofdescriptivewaystodescribetherelationshipbetweenStudentandPerson.

• WecansaythattheStudentclassisasubclassofthePersonclass,orthatPersonisasuperclassofStudent.

• WecanalsocallStudentachild-classofPerson,andPersontheparent-classofStudent.• WecanalsosaythatStudentisasubtypeofPerson,andPersonisasupertypeofStudent.

Usingtheis-aRelationship.Sohowcanweactuallyusethis"Studentis-aPerson"relationship?Considerthefollowinglegalcode(oncewe'veactuallywrittentheconstructors):Person p = new Person ("A",1); Student s = new Student("B", "CS", 20, 71235, 3); System.out.println("p name: " + p.getName()); System.out.println("s name: " + s.getName()); p = s; System.out.println("p name: " + p.getName()); ThepvariablecanonlyholdareferencetoaPersonobject.ButaStudentis-aPerson,sopcanalsoholdareferencetoaStudent,becausethatmeansitalsohappenstobestoringareferencetosomethingthatis-aPerson.Now,backtoourregisterexample.WenowknowthataPersonis-aPerson,aStudentis-aPerson,andanEmployeeis-aPerson.Soit'ssafetouseanarrayofPersonreferencestostoreourvariousvalues:Person[] customers = {pers, stud, emp}; YourTurn!

Createfilesforallthreeoftheaboveclasses.Fornow,don'twriteconstructorsyet;relyontheannoyingdefaultconstructors.Thatmeanswehavetocreateourobjectsthisway:Person p = new Person(); p.setName("Bob"); p.setAge("20"); Student s = new Student(); s.setName("Jane"); s.setAge(19); s.setMajor("art"); …

• Caution:ifyouaccidentallyaddnameandagefieldstoStudentorEmployee,Javawillallowit(itiscalledshadowing,andisgenerallyabadideatousethis'feature').Makesurethatyoudon'tre-defineanythingthatissupposedtobeinherited.

• AddanotherclassthatextendsPerson.Choosesomeothertypeofpersonthatwouldbeoncampus:Policeman,Athlete,Professor,orwhatever.EvenifitseemstooverlapwitheitherStudentorEmployee,it'sokay,aslongasthere'sextradatayouwanttokeeptrackofforthisnewtypeofperson.

• Createobjectsofeachofyourclasstypes.(PutthiscodeinTestInheritance.java).Exploreusingthegetters/setters,andseethatyoudoindeedgettousemethodsdeclaredinthePersonclasswhenusingaStudentobject,Employeeobject,andsoon.

• CreateatoStringmethodforjustthePersonclass,andnottheothers.Retestthe"UsingtheIs-ARelationship"code,butjustprintoutpandsinsteadofp.getName()andsoon.Noticethatevenmethodslikethiscanbeinherited.We'llseehowwecandobettertorepresentStudentobjectsthanbeingstuckwiththePerson versionofthetoString implementation.

VisibilityWefinallyhaveachancetoexperiencethelastvisibility:protected.TrywritingatoStringmethodfortheStudentclass.Printalltherelevantinfo.Forexample,trytogetyourtoStringmethodintheStudentclasstooutput:Student{name="foo", major="art", age=19, masonID=1234, years=2} Whatgoeswrong?WegetacomplaintfromtheJavacompilerthatnameandageareprivate.(Assumingyouusedthevisibilitiesshownabove!Iftheknee-jerkreactiontomakeeverythingpublickickedin,justgobacktoPerson,makethoseinstancevariablesprivate,andthenseethechildfailtoaccessit).Eventhoughwe'reinheritingfromthePersonclass,privatestillmeans"onlyaccessibleincodeactuallywritteninthisclassdefinition".Ratherthanmakenameandage public(whichviolatesencapsulationprinciples),weareofferedacompromise:theprotectedvisibility.Thiswillcausename andagetostillbehaveasifitwereprivateinplacessuchasyourtestingcode(insomeotherclasslikeTestInheritance),buttheywillnowbehaveasiftheywerepublicwhenaccessedfromachildclasslikeStudent. YourTurn!

• Changethevisibilityofnameandagetoprotected.o First,verifythatyoustillcan'taccessthemfromTestInheritance.Thisisbecauseit'snota

childclassofPerson.• GobackandwriteyourtoStringmethodintheStudent class.Nowthattheyareprotected,it'sas

iftheyweredeclaredwithpublicvisibilityasfarasthechildclassesareconcerned.o Revisitthe"UsingtheIs-ARelationship"examplefromabove(andstilljustprintpands

ratherthanp.getName()andsoon).Nowit'sevenmoreapparentthatwehaveaStudentobjectbeingusedwhereweexpectaPerson.

OverridingMethodsInthelastexample,wewitnessedsomethingthatisactuallykindofamazing:theStudentclassgottooverridethedefinitionofthetoStringmethod.PersonalreadyprovidedatoStringmethod,whichusedtobewhatourStudentclassused.Butwedecidedwecoulddobetter,andgotridofthatdefinitioninfavorofamorespecificone.Wecanoverridemethodsinheritedfromtheparentclass,withafewrequirements:

• Wehavetheexactsamemethodsignature,andthenwegettosupplyadifferentbody(implementation)forthemethod.

• Theparentclasshastogiveuspermissiontooverrideamethodbynotmakingthemethodfinal.Yes,methodscanbefinal,too–itmeansthatthemethodbodycannotbemodified.Soifwedon'twantre-implementationsofamethodinchildclasses,wejustsaysomethinglike public final int importantMethod(The args) andnowoverridingisnotallowed.

Wheneverourcodecallsthisoverriddenmethodonobjectsofourchildclass,itwillalwaysusethis'better'versionthatweprovided,thusoverridingtheoriginaldefinition.

YourTurn!

• AddthewakeUpTimemethodtothePersonclass,andoverrideitintheStudentclass.(JustreturnaStringforwhetherthispersonwakesupearlyorlate;I'massumingthatstudentsliketosleepin,andemployeesdon'tgetto).

• Testitout(callitonaPerson,andonaStudent).• NowmakethePersonversionofthemethodfinal,andlookforthecompilererror.

WecanactuallytellJavathatweintendtooverrideamethod:justput@Overriderightbeforeamethodthatissupposedtobeonoverridingdefinition.Ifanythinggoeswrong,suchasnotbeingallowedtooverridethemethod,oritturnsoutthatyourmethoddoesn'tactuallyoverrideanymethod,thecompilercannowalertyou.

→whenwouldwenotbeoverridingamethodwhenwethoughtwewere?Consideramethodwithsignature@Override public String tostring() .It'stryingtooverridetoString,butneglectedtocapitalizetheS.

YourTurn!

• addamethodthatisn'tinheritedtoyourStudentclass.Addthe@OverridetagandseethattheJavacompilercatchestheissue.

InheritanceandConstructorsLet'sfinallydiscusshowtomakeconstructorsforchildclasses.Asafirststep,addaniceconstructortothePersonclass:public Person (String name, int age) { this.name = name; this.age = age; } YouwillalsoneedtochangeyourPersoninstantiationstolooklikenew Person ("Bob", 20).Trytore-compile.Specifically,trytorecompilejusttheStudentclass: javac Student.java Whathappens?JavacancreatePersonobjectsusingtheconstructoryouadded,butnowthere'snodefaultconstructor,andsowecan'tevencompiletheStudentclassanymore,whichwasactuallyrelyingonit.OurPersonclassdeclaredthatitwasnolongeracceptabletousethedefaultconstructor,anditexposedthefactthatourStudentconstructoractuallyusedthePersonconstructortocompletethetaskofconstructingaStudentobject.Let'screateaconstructorfortheStudentclass.

First(Bad)Attempt.Let'strytojustwriteaStudentconstructorthatdoesn'trelyonthePersonconstructoratall. //BAD attempt at a child class constructor

public Student (String name, String major, int age, int masonID, int years){ this.name = name; this.major = major; this.age = age; this.masonID = masonID; this.yearsOnCampus = years; } Thisfails;somehow,theJavacompilerstillwantstofindtheconstructorPerson().Itturnsoutwehavetolearnanotherkeyword:super.superissimilarinnaturetothis.Thekeywordthis referstothecurrentobject,givingusaccesstovaluesandmethodsoftheobject.superreferstotheparentclass,givingusaccesstothemembersoftheparentclasswhichweotherwisemaybecouldn'tseeinthechildclass.Ifwewanttoaccessanythingintheparentclass,suchasaspecificconstructormethod,wehavetousesupertoaccessit. // GOOD attempt at a child constructor public Student (String name, String major, int age, int masonID, int years){ //call the parent constructor super(name,age); //finish instantiating things declared in this class. this.major = major; this.masonID = masonID; this.yearsOnCampus = yearsOnCampus; } Thecallsuper(name,age)isactuallycallingtheconstructorofthePersonclassthatacceptsaStringandanint.ThecalltosuperneedstobefirstinourconstructorbodysothatJavaknowswhat'sgoingon.RequiredsupercallsJavarequiresustousetheparentclass'sconstructorwhenwritingourownconstructor.Thisensuresthatourclaimthat"EveryStudentisaPerson"won'tbeviolatedbysomenaïveimplementationofaconstructor.Itturnsoutthatanimplicitsuper()callisaddedtoconstructormethodofchildclassesunlessweaddourownsuper-call.That'swhyourfirstattemptataStudentconstructorfailed:therewasnopublic Person()constructordefinitionanymore.BycreatinganewconstructorforPerson,andthenexplicitlycallingthatnewconstructorwithacalltosuper(some,args),wearefulfillingthe"alwaysuseyourparentclass'sconstructor"requirementwhilealsocompletingtheinstantiationeffortsthatarelocaltoourownclass'sinstancevariables.

YourTurn!

• Createconstructorsforallclasses.Besuretousethenon-defaultconstructorfromthePersonclassbyusingthecorrectsupercalltothePersonconstructorfromyourchildclasses'constructors.

• WhencreatingconstructorsforStudentandEmployee,rememberthatyouhavefiveinstancevariablestoinstantiate(twocamefromthePersonclass,threearedefinedhereintheclass).

• Createallthegettersandsettersfortheinstancevariablesintroducedineachclass.(Remember,theonesdefinedinthePersonclassareinheritedinthechildclasses:youwon'twritegetNameintheStudentclass,butStudentswillinheritthegetName getterthatyoudefineinthePersonclass).Willanythingneedtobemadeprotected insteadofprivate?

AbstractClassesOnelasttopicforinheritanceisthenotionofanabstractclass.Abstractclassesparticipateintheclasshierarchyofparentandchildclasses,butbymakingaclassabstract,wearestatingthatnoobjectsofthisspecificclassmayeverbecreated.(Objectsofchildclasseswouldbepermitted;otherwise,therewouldbenopoint!).Let'sconsideranexampleofanabstractclass.Ifwelooktoouroriginalthreeclasses,wesawaslightchanceformoreoverlap:theStudentandEmployeeclassessharedinstancevariablesmasonIDandyearsOnCampus.Let'screateaclasstohelpusindicatethatrelationshipbyleavingthePersonclassalone,butintroducinganewclassbetweenthePersonclassandthetwochildclassesStudentandEmployee:public class MasonPerson extends Person { protected int masonID; protected int yearsOnCampus; public MasonPerson(String name,int age,int masonID,int years) { super(name, age); this.masonID = masonID; this.yearsOnCampus = years; } }

public class Student extends MasonPerson { protected String major; public Student(String name, String major, int age, int masonID, int years){ //calls MasonPerson constructor super(name,age,masonID,years); this.major = major; } //no more variables, only methods below… } public class Employee extends MasonPerson { protected String jobTitle; public Employee (String name, String job, int age, int masonID, int years){ //calls MasonPerson constructor super(name, age, masonID, years); this.jobTitle = job; } //no more variables, only methods below… }

Onenicethingisthetransitivityofinheritance:becauseaStudentis-aMasonPersonandaMasonPersonis-aPerson,wecantransitivelyclaimthataStudentis-aPerson.Allthedata/behaviorthatMasonPersoninheritsfromPersonwillbepassedontoStudent.Ontoabstractclasses.Suppose,forwhateverreason,thatcreatingaMasonPersondoesn'tmakesense:weonlywanttheretobeobjectsofspecifickindsofpeople:StudentobjectsandEmployeeobjectsareokay,butthevagueideaofaMasonPersononlyhelpsusorganizeourthoughts;anyMasonPersonmustactuallybesomemorespecifictype.WethenchoosetomaketheMasonPersonclassabstract,indicatingthatitisillegaltoinstantiatetheclass:public abstract class MasonPerson extends Person { … } YourTurn!

• AddtheMasonPersonclass,whichextendsthePersonclass.o AddinstancevariablesmasonIDandyearsOnCampus.o Giveitaconstructor,usingthePersonconstructor.

• ChangeStudentandEmployeetobechildclassesofMasonPerson.UpdatetheirconstructorstousetheMasonPersonconstructor.

• InyourTestInheritanceclass,createaMasonPersonobjectandtestitabit.• MakeMasonPersonanabstractclass;re-compileTestInheritanceandseetheerrormessage.→you'vesuccessfullycreatedanabstractclassthatcontributestotheclasshierarchy,yetissafelynotinstantiable(wecan'tcreateobjectsofit).

(keepgoing!Bigchart+descriptionallonthenextpage→)

AnotherAbstractClassesExampleAnalternateexamplecouldbeaprogramrepresentingazoo,whereweusetheKingdom-Phylum-Class-Order-Family-Specieshierarchytoorganizeourclassesofanimals.WewanttohaveclassesforMonkey,Orangutan,Panda,Dolphin,Penguin,Dove,Frog,Alligatorandmoreanimals.Itmakessensetoaddclassestogeneratethefollowinghierarchy:

ItmakessensetohaveMonkey,Panda,andDolphinobjects,butitdoesn'tmakesensetohaveaMammalobjectoraChordataobject.Thoseclassesjusthelpedusorganizethings,andmaybeprovidedefinitionslikenumVertebra,numChromosomes,eggSize,andsoon.Ifwemakealltheblueclassesabstract,theystillparticipateinthehierarchyoftypes(andcancontributefieldsandmethodsforinheriting),butareguaranteedtonotbeinstantiatedthemselves.MoreAbstractThingsWecanusetheabstractkeywordformethods,too.Whatisanabstractmethod?Itisamethodsignaturewithnobody:public abstract int reportNumberOfLegs(); public abstract void moveAround(); Anytimewewanttoindicatethateverychildclassofourabstractclassmusthaveitsownversionofamethod,yetthere'snotgoodstartingimplementationrightnowintheabstractclass,wecancreatean

abstractmethodtorequirethatallchildclassesprovideimplementations(byoverwritingthem).Iftheydon't,theybecomeabstractaswell,guaranteeingthatthismethodwillbeimplementedforanyactualinstanceofaclassthatisachildoftheabstractclassthatintroducedthisabstractmethod.

• Anyclasscontaininganabstractmethodmustbeabstractaswell.• Anyclassthatinheritsanabstractmethodcanimplementitbyoverwritingthatdefinition.• Anyclassthatdoesn'timplementaninheritedabstractmethodthereforestillcontainsanabstract

method,andisthusalsoabstract. YourTurn!

• NowthatMasonPersonisanabstractclass,addanabstractmethodtoit,suchas: public abstract String favoriteFoodSite();

• TryinstantiatingaStudentandseethatit'snowbecomingabstract(well,weseecompilation

erroscomplainingtothateffect).• Overridetheabstractmethodthatwasinherited,andnowweagainareabletoinstantiateour

Studentclass.